断点续传
思路
- 利用 file.slice 对文件切片
- 每次上传前向后台确认已上传的进度(即获取已上传的文件列表,避免重复上传),文件列表的长度即默认进度条长度,没有就是0
- 继续上传未上传的文件(每成功上传一个文件,进度条 + 1),当全部文件上传完毕,发起合并文件请求
实例
(function () {
const changeBuffer = file => {
return new Promise(resolve => {
let fileReader = new FileReader();
fileReader.readAsArrayBuffer(file);
fileReader.onload = ev => {
let buffer = ev.target.result,
spark = new SparkMD5.ArrayBuffer(),
HASH,
suffix;
spark.append(buffer);
HASH = spark.end();
suffix = /\.([a-zA-Z0-9]+)$/.exec(file.name)[1];
resolve({
buffer,
HASH,
suffix,
filename: `${HASH}.${suffix}`
});
};
});
};
upload_inp.addEventListener('change', async function () {
let file = upload_inp.files[0];
if (!file) return;
let already = [],
data = null,
{
HASH,
suffix
} = await changeBuffer(file);
try {
data = await instance.get('/upload_already', {
params: {
HASH
}
});
if (+data.code === 0) {
already = data.fileList;
}
} catch (err) {}
let max = 1024 * 100,
count = Math.ceil(file.size / max),
index = 0,
chunks = [];
if (count > 100) {
max = file.size / 100;
count = 100;
}
while (index < count) {
chunks.push({
file: file.slice(index * max, (index + 1) * max),
filename: `${HASH}_${index+1}.${suffix}`
});
index++;
}
index = 0;
const complate = async () => {
index++;
upload_progress_value.style.width = `${index/count*100}%`;
if (index < count) return;
upload_progress_value.style.width = `100%`;
try {
data = await instance.post('/upload_merge', {
HASH,
count
}, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
});
if (+data.code === 0) {
alert(`恭喜您,文件上传成功,您可以基于 ${data.servicePath} 访问该文件~~`);
clear();
return;
}
throw data.codeText;
} catch (err) {
alert('切片合并失败,请您稍后再试~~');
clear();
}
};
chunks.forEach(chunk => {
if (already.length > 0 && already.includes(chunk.filename)) {
complate();
return;
}
let fm = new FormData;
fm.append('file', chunk.file);
fm.append('filename', chunk.filename);
instance.post('/upload_chunk', fm).then(data => {
if (+data.code === 0) {
complate();
return;
}
return Promise.reject(data.codeText);
}).catch(() => {
alert('当前切片上传失败,请您稍后再试~~');
clear();
});
});
});
})();