大文件上传时可以将文件利用Blob对象的slice方法切分为若干chunk,并发发送upload请求(使用spark-md5生成hash,作为文件的唯一标识),服务器端将接收到的chunk保存在相应的文件夹中;切片上传完成后发送merge_chunk 请求,通知服务器端合并切片,生成最终的文件。
github: github.com/kythen/expr…
主要代码:
js
const {fileList} = this.state;
const {name} = values;
const file = fileList[0];
const promiseList = [];
const chunkSize = 4 * 1024 * 1024;
const chunkCount = Math.ceil(file.size / chunkSize);
const hash = await this.hasFile(file);
for (let i = 0; i < chunkCount; i++) {
const start = i * chunkSize;
const end = Math.min(file.size, start + chunkSize);
const formData = new FormData();
formData.append('file', file.slice(start, end));
formData.append('name', file.name);
formData.append('total', chunkCount);
formData.append('size', file.size);
formData.append('index', i);
formData.append('hash', hash);
promiseList.push(callApi('post', `${prefix}/upload`, '', formData));
}
Promise.all(promiseList).then(res => {
this.setState({loading: false});
const data = {
size: file.size,
name: file.name,
total: chunkCount,
hash
};
callApi('post', `${prefix}/merge_chunks`, '', data).then(res => {
alert('上传成功');
})
});
node
router.post('/upload', upload.single('file'), function(req, res, next) {
const {
name,
size,
total,
hash,
index,
file
} = req.body;
const chunksPath = path.join(uploadPath, hash, '/');
if (!fs.existsSync(chunksPath)) mkdirsSync(chunksPath);
fs.renameSync(req.file.path, chunksPath + hash + '-' + index);
res.json({message: 'SUCCEED'});
});
router.post('/merge_chunks', function(req, res, next) {
const {
size,
name,
hash,
total
} = req.body;
const chunksPath = path.join(uploadPath, hash, '/');
const chunks = fs.readdirSync(chunksPath);
const filePath = path.join(uploadPath, name);
fs.writeFileSync(filePath, '');
if (chunks.length !== total || chunks.length === 0) {
res.status = 200;
res.end('切片数量不符合');
}
for(let i = 0; i < total; i++) {
fs.appendFileSync(filePath, fs.readFileSync(chunksPath + hash + '-' + i));
fs.unlinkSync(chunksPath + hash + '-' + i);
}
fs.rmdirSync(chunksPath);
res.json({message: 'SUCCEED'});
});ps: 断点续传是基于分片上传的,由于网络故障或用户主动暂停后,下次上传会跳过已经上传的部分,只上传还为上传的部分,有机会再更新断点续传吧。。