在網(wǎng)上看了大半天mate60 現(xiàn)在耳朵里都是遙遙領(lǐng)先, 所以一鼓作氣給大家分享一篇大文件分片+webWorker的文章, 通過寫作這篇文章, 自己也學習到了不少知識, 比如通過js獲取電腦cpu線程, 那么話不多說直接開始
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>大文件分片</title>
<style></style>
</head>
<body>
<input type="file" id="fileRef" />
<script type="module" src="./main.js">
</script>
</body>
</html>
這里使用的是SparkMD5進行加密, 可自行下載并導入
import { createChunks } from './createChunks'
// 規(guī)定每次切片的文件大小
const CHUNK_SIZE=1024 * 1024 * 5 // 5MB
export async function cutFile (file) {
// 生成每一個切片, 分片是耗時的所以是異步操作
const chunk=await createChunks(file, 1, CHUNK_SIZE)
// 等待分片完成, 就可以拿到這一個分片的信息
console.log(chunk)
}
定義cutFile函數(shù)來進行文件切片, 并且返回該切片的開始結(jié)束, 第幾個和hash值
import SparkMD5 from "./md5.js";
/**
* @param {*} file 文件
* @param {*} index 第幾個分片
* @param {*} chunkSize 每一個分片的大小
*/
export function createChunks (file, index, chunkSize) {
return new Promise((resolve, reject)=> {
// 開始第幾個*分片的大小
const start=index * chunkSize
// 結(jié)束時start + 分片的大小
const end=start + chunkSize
const fileReader=new FileReader()
spark.append(e.target.result);
const files=file.slice(start, end);
// 讀取文件的分片 讀取完成后觸發(fā)onload事件
fileReader.onload=e=> {
console.log(e)
const spark=Md5(e.target.result)
resolve({
start,
end,
index,
hash: spark.end(),
files,
})
}
// 讀取文件的分片
fileReader.readAsArrayBuffer(file.slice(start, end))
})
}
現(xiàn)在我們只讀到了一個分片, 但是我們需要讀取所有的分片, 所以需要計算目前這個文件需要分成多少個分片, 然后一個一個去讀
所以可以利用文件的總大小 / 自定義定義文件切片的大小 就可以得到總切片數(shù)量
注意這里需要向上去整, 出現(xiàn)任何的小數(shù)點都需要包含一片內(nèi)
export async function cutFile (file) {
// 計算文件的切片數(shù)量
const chunks=Math.ceil(file.size / CHUNK_SIZE)
console.log(chunks, 'chunks')
// 生成每一個切片, 分片是耗時的所以是異步操作
const chunk=await createChunks(file, 1, CHUNK_SIZE)
// 等待分片完成, 就可以拿到這一個分片的信息
console.log(chunk)
}
已經(jīng)拿到了文件切片的總數(shù)量, 這里就可以循環(huán), 并存儲到一個數(shù)組中即可
export async function cutFile (file) {
const result=[]
// 計算文件的切片數(shù)量
const chunks=Math.ceil(file.size / CHUNK_SIZE)
// 生成每一個切片, 分片是耗時的所以是異步操作
for (let i=0; i < chunks; i++) {
const chunk=await createChunks(file, i, CHUNK_SIZE)
// 等待分片完成, 就可以拿到這一個分片的信息
result.push(chunk)
}
return result
}
此時我們根據(jù)入口函數(shù)來打印看一下結(jié)果
文件被切割成了103分并且都保存在了一個數(shù)組中, 消耗了2.3秒
這里我上傳的文件是500m大小, 但是我上傳是好幾個g 一定會更加的延遲, 造成線程長時間的阻塞, 原因是這里使用到了MD5, 進行了大量的計算
所以如何避免線程的阻塞就是優(yōu)化的關(guān)鍵, 在js中有Web Worker
mdn的概念說: 使得在一個獨立于 Web 應(yīng)用程序主執(zhí)行線程的后臺線程中運行腳本操作成為可能。這樣做的好處是可以在獨立線程中執(zhí)行費時的處理任務(wù),使主線程(通常是 UI 線程)的運行不會被阻塞/放慢。
關(guān)于webWoker的使用, 推薦大家看一下BEFE團隊 的一文徹底學會使用web worker
首先需要定義開始線程的數(shù)量, 并且按照數(shù)量去開啟新的線程, 并搭設(shè)架子
至于要向worker發(fā)送什么消息, 以及接收返回的值, 我們稍后分析
// 定義線程數(shù)量
const THREAD_COUNT=4 // 4個線程
export async function cutFile (file) {
const result=[]
// 計算文件的切片數(shù)量
const chunks=Math.ceil(file.size / CHUNK_SIZE)
// 生成每一個切片, 分片是耗時的所以是異步操作
// for (let i=0; i < chunks; i++) {
// const chunk=await createChunks(file, 1, CHUNK_SIZE)
// // 等待分片完成, 就可以拿到這一個分片的信息
// result.push(chunk)
// }
// 創(chuàng)建新的線程
for (let i=0; i < THREAD_COUNT; i++) {
const worker=new Worker('./worker.js', { type: 'module' })
worker.postMessage(???)// 向 worker 線程發(fā)送消息
worker.onmessage=e=> {
// 接收到 worker 線程返回的消息
console.log(e)
}
}
return result
}
以上三個條件已知的是file文件以及每一個文件的chunksize, 只有第三個是不知道的, 也就是不知道開始和結(jié)束值的分片期間
首先需要計算每一個線程需要處理的切片數(shù)量
const workerChunkCount=Math.ceil(切片數(shù)量 / 定義線程數(shù)量)
開啟線程, 并計算每個線程的開始索引和結(jié)束索引, 這里需要在遍歷中通過i下標進行計算
// 這里的worker稍后解釋如何定義, 注意這里要寫type: module 因為引入了其他模塊需要使用
const worker=new Worker('./worker.js', { type: 'module' })
// 計算每個線程的開始索引和結(jié)束索引
const startIndex=i * workerChunkCount
let endIndex=startIndex + workerChunkCount
// 防止最后一個線程結(jié)束索引大于文件的切片數(shù)量的總數(shù)量
if (endIndex > chunks) {
endIndex=chunks
}
最后進行發(fā)送
// 向 worker 線程發(fā)送消息
worker.postMessage({
file, // 文件
CHUNK_SIZE, // 文件大小
startIndex, // 開始下標
endIndex // 結(jié)束下標
})
完整代碼
// 規(guī)定每次切片的文件大小
const CHUNK_SIZE=1024 * 1024 * 5 // 5MB
// 定義線程數(shù)量
const THREAD_COUNT=4 // 4個線程
export async function cutFile (file) {
const result=[]
// 計算文件的切片數(shù)量
const chunks=Math.ceil(file.size / CHUNK_SIZE)
// 計算每一個線程需要處理的切片數(shù)量
const workerChunkCount=Math.ceil(chunks / THREAD_COUNT)
// 生成每一個切片, 分片是耗時的所以是異步操作
// for (let i=0; i < chunks; i++) {
// const chunk=await createChunks(file, 1, CHUNK_SIZE)
// // 等待分片完成, 就可以拿到這一個分片的信息
// result.push(chunk)
// }
// 創(chuàng)建新的線程
for (let i=0; i < THREAD_COUNT; i++) {
const worker=new Worker('./worker.js', { type: 'module' })
// 計算每個線程的開始索引和結(jié)束索引
const startIndex=i * workerChunkCount
let endIndex=startIndex + workerChunkCount
// 防止最后一個線程結(jié)束索引大于文件的切片數(shù)量的總數(shù)量
if (endIndex > chunks) {
endIndex=chunks
}
// 向 worker 線程發(fā)送消息
worker.postMessage({
file, // 文件
CHUNK_SIZE, // 文件大小
startIndex, // 開始下標
endIndex // 結(jié)束下標
})
worker.onmessage=e=> {
// 接收到 worker 線程返回的消息
console.log(e)
}
}
return result
}
let finishCount=0 // 記錄線程開啟的次數(shù)
worker.onmessage=(e)=> {
// 接收到 worker 線程返回的消息
for (let i=startIndex; i < endIndex; i++) {
result[i]=e.data[i - startIndex];
}
worker.terminate();
finishCount++;
// 如果記錄的開啟線程=定義的開啟線程次數(shù)
if (finishCount===THREAD_COUNT) {
// 通知主線程, 并返回結(jié)果
resolve(result);
}
};
這里使用resolve 因為需要等待, 所以需要是異步操作
完整代碼
export const cutFile=async (file)=> {
return new Promise((resolve, reject)=> {
// 規(guī)定每次切片的文件大小
const CHUNK_SIZE=1024 * 1024 * 5; // 5MB?
// 定義線程數(shù)量
const THREAD_COUNT=4; // 4個線程
let result=[];
// 計算文件的切片數(shù)量
const chunks=Math.ceil(file.size / CHUNK_SIZE);
// 計算每一個線程需要處理的切片數(shù)量
const workerChunkCount=Math.ceil(chunks / THREAD_COUNT);
let finishCount=0; // 完成的線程數(shù)量
// 生成每一個切片, 分片是耗時的所以是異步操作
// for (let i=0; i < chunks; i++) {
// const chunk=await createChunks(file, i, CHUNK_SIZE);
// // 等待分片完成, 就可以拿到這一個分片的信息
// result.push(chunk);
// }
// return result;
// 創(chuàng)建新的線程
for (let i=0; i < THREAD_COUNT; i++) {
const worker=new Worker("./worker.js", {
type: "module",
});
// const worker=new Worker(worderPath)
// 計算每個線程的開始索引和結(jié)束索引
const startIndex=i * workerChunkCount;
let endIndex=startIndex + workerChunkCount;
// 防止最后一個線程結(jié)束索引大于文件的切片數(shù)量的總數(shù)量
if (endIndex > chunks) {
endIndex=chunks;
}
worker.postMessage({
file,
CHUNK_SIZE,
startIndex,
endIndex,
});
worker.onmessage=(e)=> {
// 接收到 worker 線程返回的消息
for (let i=startIndex; i < endIndex; i++) {
result[i]=e.data[i - startIndex];
}
worker.terminate();
finishCount++;
if (finishCount===THREAD_COUNT) {
// 所有線程都完成了
// 通知主線程
// console.log(result);
resolve(result);
}
};
}
});
};
因為這里開啟了worker線程, 所以需要執(zhí)行./worker的邏輯
const worker=new Worker("./worker.js", {
type: "module",
});
很顯然 這里就是將file,CHUNK_SIZE,startIndex,endIndex 這幾個有用的數(shù)據(jù)拿出來, 并且執(zhí)行讀取單個分片的方法 createChunks 這里之前已經(jīng)定義好了, 所以直接調(diào)用
// 之前 添加worker的信息
worker.postMessage({
file,
CHUNK_SIZE,
startIndex,
endIndex,
});
按照以上這兩點思路, 就可以寫邏輯了
import { createChunks } from "./createChunks.js";
onmessage=async (e)=> {
const arr=[];
const { file, CHUNK_SIZE, startIndex, endIndex }=e.data;
// console.log(file, CHUNK_SIZE, startIndex, endIndex);
for (let i=startIndex; i < endIndex; i++) {
arr.push(createChunks(file, i, CHUNK_SIZE));
}
// Promise.all=> 同時進行異步操作
const chunks=await Promise.all(arr);
// 提交線程信息
postMessage(chunks);
};
看一下結(jié)果吧
很明顯, 從之前的2秒多 多現(xiàn)在的0.2米, 速度快了10倍.
突發(fā)奇想, js能不能獲取電腦cpu的最大線程數(shù)量呢, 這樣就可以每次追求最快的速度, 現(xiàn)在已經(jīng)是晚上1點多了, 偷懶直接問了chat, 哈哈 搜了一下還真有
這里果斷再優(yōu)化一下, 最求卓越
// 獲取核心線程的數(shù)量
const THREAD_COUNT=navigator.hardwareConcurrency || 4;
console.log(navigator.hardwareConcurrency);
果然又快了一倍多, 美滋滋~手動撒花~~??ヽ(°▽°)ノ?
最近華為mate60的突然發(fā)布, 是讓很多人包括我在內(nèi)都欣喜不已!華為伴著遙遙領(lǐng)先和麒麟芯片再次回來了! 近年來,華為面臨了來自美國政府的技術(shù)制裁,但這并沒有阻止華為繼續(xù)前行,如同一葉輕舟,穿越千山萬水,創(chuàng)造著無盡可能. 有網(wǎng)友拍攝深夜的華為總部, 總是燈火通明, 里面的人都是頂級的優(yōu)秀人才. 不僅優(yōu)秀更是勤勞付出.
年底爭取給 給自己換一臺華為手機. #華為 #麒麟芯片 #科技強國
原文鏈接:https://juejin.cn/post/7273803674789953575
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。
數(shù)據(jù)獲取是任何應(yīng)用程序中最重要的部分,本文將介紹,如何在react, nextjs中獲取數(shù)據(jù)
主要有種方法可以獲取數(shù)據(jù)
下面分別介紹一下這4種獲取數(shù)據(jù)的方式
nextjs擴展了本機的fetch WEB API , 所以可以在服務(wù)端的每個請求上配置緩存和重新校驗的行為
你可以在服務(wù)端組件中使用 async, await 。
下面是獲取一個頁面的例子:
export default async function Page() {
const res=await fetch("https://www.helloworld.net");
const data=await res.text();
return (
<div>
<p>{data}</p>
</div>
);
}
默認情況下,服務(wù)端執(zhí)行異步函數(shù),fetch 是不緩存數(shù)據(jù)的(默認配置:cache: no-store)
緩存數(shù)據(jù)是提升性能一種很好的方式 ,如何配置呢 ? 比如上面的例子
fetch('https://www.helloworld.net', { cache: 'force-cache' })
但是在下面兩種情況下,是不緩存數(shù)據(jù)的。
可以給緩存添加一個過期時間,如下:
fetch('https://www.helloworld.net', { next: { revalidate: 3600 } })
上一節(jié)的文章中,我們就講過,nextjs 是可以直接寫接口的。
如何寫這樣一個接口呢?步驟如下:
以上步驟主要目的就是創(chuàng)建一個文件: rootDir/app/chat/route.ts
在route.ts文件中,添加如下代碼:
import {NextApiRequest, NextApiResponse} from 'next';
import {NextResponse} from "next/server";
export const GET=async (request: NextApiRequest, context: any)=> {
const {params}=context;
try {
return NextResponse.json({
status: 200,
data:{
site:'www.helloworld.net',
author:'待兔'
}
});
} catch (e: any) {
return NextResponse.json({status: 500, message: e.message});
}
}
在瀏覽器中訪問http://localhost:3000/api/chat
響應(yīng)如下:
類比以上的操作,可以創(chuàng)建其它的api, 這個就是路由處理器,英文叫 Route Handlers , 也許我翻譯的并不準確
以上就是經(jīng)常用的獲取數(shù)據(jù)的方式,如果對你有幫助,請點個關(guān)注轉(zhuǎn)發(fā)一下。謝謝
個視頻來說一下js的文件讀取。剛好有同學問到,首先要說一點,對文件進行讀寫、增刪、改查一系列的操作是涉及到用戶隱私安全的。用前端語言,比方JS是不可能完全做到的。要不然寫一個網(wǎng)頁,訪問了就可以在本地電腦上面隨便新增文件,或者把電腦里面的東西全部給干了或者改了,都是一件很恐怖的事情。
可以實現(xiàn)文件操作的語言,比方nodejs,一些后端語言,PHP、JAVA這些。這個時候要分清楚是操作哪里的文件,是客戶端還是服務(wù)器端。如果要操作客戶端的,都要獲取相應(yīng)的權(quán)限。比方手機端的應(yīng)用都會向我們拿權(quán)限。很多時候不給文件訪問的權(quán)限,比方圖庫,連相冊訪問都訪問不了,不用增刪、改查了,這些先不管它了。
JS的文件讀取,簡單的文件讀取還是可以實現(xiàn)的。但是同樣,這里要讀取的是本地的文件,其實也需要用戶給予權(quán)限。這里要怎么做?就需要配合一個input的控件,讓用戶自己來操作。這里提供了一個按鈕,只有用戶主動去選擇了,這個行為是用戶觸發(fā)的。換言之就是得到了用戶的允許才會對文件進行讀取,不會JS自動觸發(fā)、自動讀取,那就不安全了。
來看一下具體怎么操作。要對input添加一個onchange事件,然后執(zhí)行一個函數(shù)a1s,把事件對象傳進去,然后來寫這個函數(shù)。這里需要用到一個fileReader類,先把對象實例化出來。可以先把事件對象打印出來看一下。先把控制臺打開,然后來選擇文件。這里選擇a.txt文件,事件對象打印出來了,展開來看一下。
里面有個target,target下面還有一個叫files,這一個下標為0的,files里面下標為0的,放的就是剛才選擇的文件a.txt,所以就要讀取用戶選擇的文件。這里是可以讀取成好幾種格式的,就把它讀成文本。
·要讀取什么?就是剛才給大家展開來看的target下面files的第0個,就是剛才選擇的文件a.txt,這個文件里面就隨便輸入一句話,看一下等一下可不可以把它讀取出來。這個過程是異步的,要先等它讀取。
·這里面還是先把事件對象打出來看一下,同樣是選擇剛才那個文件,再展開來看一下,同樣找到target,它里面有一個result,大家看到?jīng)]有,result里面保存的就是讀取的文件里面的內(nèi)容,這樣就可以讀取到文件的內(nèi)容了。
·比方這里直接把讀取到的內(nèi)容輸出來,沒有問題,這個內(nèi)容獲取到了。當然除了讀這種txt的文本,讀個json,甚至讀個圖片也可以,就來讀個圖片試一下。讀圖片這里就不是讀成text,讀成base64。
·這次就來選擇一張圖片,這里就把讀取到的結(jié)果輸出來了,可以復制一下,這個是base64,可以把剛才復制的那一串在地址欄里面回車,這張圖片就出來了,是可以讀取到這張圖片的。
·可以用讀取到的圖片做一些預(yù)覽圖之類的,這個圖片有點大,給它改小一點。JS獲取到img,然后設(shè)置一下它的src,把result賦值給它。
·現(xiàn)在還沒有預(yù)覽圖,可以手動來選擇一張,這里隨便選擇一張圖片,這個圖片就讀取到了,然后換一張圖片也可以,另外再選擇另外一張,都是可以正常讀取的。
這個視頻就簡單給大家說了一下JS的文件讀取,感謝大家的收看。