大文件分片上传

大文件上传 第一种:不考虑使用多线程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
const inputFile = document.querySelector('input[type="file"]');

inputFile.onchange = async function(e) {
const file = e.target.files[0];
console.log('开始切片。。。')
const chunks = await cutFile(file)
console.log('结束切片',chunks)
}

//定义每份切片的大小
const CHUNK_SIZE = 1024 * 1024 * 5 //5MB


/**
* 方式1: 不考虑多线程方案
*/
async function cutFile(file) {
//一共分多少片。向上取整
const chunkCount = Math.ceil(file.size / CHUNK_SIZE)
const result = []
console.log('切片数量',chunkCount)

for(let i=0; i<chunkCount; i++) {
const chunks =await createChunk(file,i,CHUNK_SIZE)
result.push(chunks)
}
console.log('result',result)
return result

}


/**
* //接受3个参数 整个file对象 ,第几个分片,每个分片的大小
* 返回每个的分片结果
* */
function createChunk(file, index, chunkSize) {
return new Promise((resolve)=>{
//本次开始和结束切片从XX MB - XX MB
const start = index * chunkSize
const end = start + chunkSize

const fileReader = new FileReader()
//本次切片的BLOB
const blob = file.slice(start,end)
fileReader.onload = function(e) {
resolve({
start,
end,
index,
blob
})
}

fileReader.readAsArrayBuffer(blob)

})
}


大文件上传 第二种:使用多线程(worker)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
const inputFile = document.querySelector('input[type="file"]');

inputFile.onchange = async function(e) {
const file = e.target.files[0];
console.log('开始切片。。。')
const chunks = await cutFile(file)
console.log('结束切片',chunks)

}
//定义每份切片的大小
const CHUNK_SIZE = 1024 * 1024 * 5 //5MB


/**
* 方式2:多线程方案
* 让分片任务去其他线程运算。减少浏览器卡顿时间。
*/

//看你目前电脑CPU有多少核。
//
const THREAD_COUNT = navigator.hardwareConcurrency || 4 //默认4核(最低的电脑配置)

async function cutFile(file) {
return new Promise((reslove,reject)=>{

//一共分多少片。向上取整
const chunkCount = Math.ceil(file.size / CHUNK_SIZE)
//每个线程需要分到多少个分片
const threadChunkCount = Math.ceil(chunkCount / THREAD_COUNT)

console.log('你的电脑是' + THREAD_COUNT + '核') //8
console.log('此文件一共有' + chunkCount +'个分片') //22
console.log('CPU每核需要处理' + threadChunkCount + '个分片') //3

//假射你的电脑是8核的(THREAD_COUNT)。这个8核要处理20个分片(chunkCount)
//那么我们就要让每个核处理 threadChunkCount 个分片 Math.ceil(20 / 8) = 3
// 那么到了最后一个分片 如果超出了分片总数,就取分片总数(chunkCount)

const result = []
let finishCount = 0;

for(let i=0 ; i<THREAD_COUNT; i++ ) {
//创建一个线程并分配任务
const worker = new Worker('./workers.js',{
type:"module" //设置MODULE 可以让worker.js中import 其他函数,我这里都写到一个文件了,没这么用
})

const start = i * threadChunkCount
let end = (i+1) * threadChunkCount
if(end > chunkCount) end = chunkCount;

worker.postMessage({
file,
CHUNK_SIZE,
startChunkIndex: start,
endChunkIndex: end
})

worker.onmessage = (e)=>{
//因为多线程同时进行任务。所以如果要保证返回的顺序需要指定下标
for (let i = start; i<end; i++) {
result[i] = e.data[i-start]
}

worker.terminate()
finishCount ++;


// 如果你8个线程都完事了 结束
if(finishCount == THREAD_COUNT) {

reslove(result)
}

}

}

})

}



worker.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// import {createChunk} from './newcar.js'



function createChunk(file, index, chunkSize) {
return new Promise((resolve)=>{
//本次开始和结束切片从XX MB - XX MB
const start = index * chunkSize
const end = start + chunkSize

const fileReader = new FileReader()
//本次切片的BLOB
const blob = file.slice(start,end)
fileReader.onload = function(e) {
resolve({
start,
end,
index,
blob
})
}

fileReader.readAsArrayBuffer(blob)

})
}



onmessage = async (e) => {
const {
file,
CHUNK_SIZE,
startChunkIndex,
endChunkIndex
} = e.data

console.log({
file,
CHUNK_SIZE,
startChunkIndex,
endChunkIndex
})

const proms = []

for(let i=startChunkIndex; i < endChunkIndex; i++) {
proms.push(createChunk(file,i,CHUNK_SIZE))
}

const chunks = await Promise.all(proms)

postMessage(chunks)

}