思路
- 服务器端提供 分片传输
- 浏览器端提供 多线程下载
- http协议支持 206
服务端代码
router.get('/api/rangeFile', async(ctx) => {
const { filename } = ctx.query;
const { size } = fs.statSync(path.join(__dirname, './static/', filename));
const range = ctx.headers['range'];
if (!range) {
ctx.set('Accept-Ranges', 'bytes');
ctx.body = fs.readFileSync(path.join(__dirname, './static/', filename));
return;
}
const { start, end } = getRange(range);
if (start >= size || end >= size) {
ctx.response.status = 416;
ctx.body = '';
return;
}
ctx.response.status = 206;
ctx.set('Accept-Ranges', 'bytes');
ctx.set('Content-Range', `bytes ${start}-${end ? end : size - 1}/${size}`);
ctx.body = fs.createReadStream(path.join(__dirname, './static/', filename), { start, end });
浏览器端代码
// script
function downloadRange(url, start, end, i) {
return new Promise((resolve, reject) => {
const req = new XMLHttpRequest();
req.open("GET", url, true);
req.setRequestHeader('range', `bytes=${start}-${end}`)
req.responseType = "blob";
req.onload = function (oEvent) {
req.response.arrayBuffer().then(res => {
resolve({
i,
buffer: res
});
})
};
req.send();
})
}
// 合并buffer
function concatenate(resultConstructor, arrays) {
let totalLength = 0;
for (let arr of arrays) {
totalLength += arr.length;
}
let result = new resultConstructor(totalLength);
let offset = 0;
for (let arr of arrays) {
result.set(arr, offset);
offset += arr.length;
}
return result;
}
download2.onclick = () => {
axios({
url,
method: 'head',
}).then((res) => {
// 获取长度来进行分割块
console.time("并发下载");
const size = Number(res.headers['content-length']);
const length = parseInt(size / m);
const arr = []
for (let i = 0; i < length; i++) {
let start = i * m;
let end = (i == length - 1) ? size - 1 : (i + 1) * m - 1;
arr.push(downloadRange(url, start, end, i))
}
Promise.all(arr).then(res => {
const arrBufferList = res.sort(item => item.i - item.i).map(item => new Uint8Array(item.buffer));
const allBuffer = concatenate(Uint8Array, arrBufferList);
const blob = new Blob([allBuffer], {type: 'image/jpeg'});
const blobUrl = URL.createObjectURL(blob);
const aTag = document.createElement('a');
aTag.download = '360_0388.jpg';
aTag.href = blobUrl;
aTag.click();
URL.revokeObjectURL(blob);
console.timeEnd("并发下载");
})
})
}
from : blog.csdn.net/frontend_fr…