HTTP断点下载功能
1).HTTP 请求头 Range
Range: bytes=start-end
Range: bytes=10- :第10个字节及最后个字节的数据
Range: bytes=40-100 :第40个字节到第100个字节之间的数据.
示例:
表示从第0个字节开始下载
conn.addRequestProperty(“range”, “bytes=” + 0 + “-“);
注意:若支持range分段下载,服务端需返回206状态码。
nginx默认支持range分段下载。
tomcat默认也支持range分段下载
2).响应头
Content-Range
Content-Range: bytes 0-10/3103
这个表示,服务器响应了前(0-10)个字节的数据,该资源一共有(3103)个字节大小。
然后要记录当前已经下载了多少字节。
小封装
import * as path from 'path';
import * as fs from 'fs';
import axios from 'axios';
const { CancelToken } = axios;
class StreamDownload {
constructor() {
// 初始化
this.initialize();
}
// 下载函数
async downloadFile(downloadFileName, patchUrl, baseDir, receivedBytes, callback) {
this.downloadCallback = callback; // 注册回调函数
this.downloadFileName = downloadFileName;// 下载名字
this.baseDir = baseDir;
this.patchUrl = patchUrl;
this.receivedBytes = receivedBytes || 0;
try {
const req = await axios({
method: 'GET',
url: patchUrl,
responseType: 'stream',
headers: {
Range: `bytes=${this.receivedBytes}-`,
},
cancelToken: this.source.token,
});
const out = fs.createWriteStream(path.join(this.baseDir, this.downloadFileName), {
start: this.receivedBytes,
flags: this.receivedBytes > 0 ? 'r+' : 'w',
});
req.data.pipe(out);
this.totalBytes = parseInt(req.data.headers['content-length'], 10);
fs.writeFile(path.join(this.baseDir, 'info'), JSON.stringify({ url: this.patchUrl, receivedBytes: this.receivedBytes, totalBytes: this.totalBytes }), () => {});// 记录一下当前下载信息
req.data.on('data', (chunk) => {
// 更新下载的文件块字节大小
this.receivedBytes += chunk.length;
this.showProgress(this.receivedBytes, this.totalBytes);
});
// // 读取返回数据
req.data.on('end', () => {
console.log('结束了');
this.downloadCallback('finished', '');
if (this.receivedBytes >= this.totalBytes) {
fs.writeFile(path.join(this.baseDir, 'info'), JSON.stringify({ url: this.patchUrl, receivedBytes: this.receivedBytes, totalBytes: this.totalBytes }), () => {});// 记录一下当前下载信息
}
this.initialize();
});
} catch (error) {
console.log(error);
this.downloadCallback('error', '');
}
}
// 初始化
initialize() {
this.baseDir = '';
this.patchUrl = '';
this.downloadFileName = ''; // 下载文件名称,也可以从外部传进来
this.downloadCallback = null;// //回调函数
this.totalBytes = null;// 总字节
this.receivedBytes = 0;// 已经接受的字节
this.source = CancelToken.source();
}
// 下载进度
showProgress(received, total) {
const percentage = Number(((received * 100) / total).toFixed(1));
// 用回调显示到界面上
this.downloadCallback('progress', percentage);
}
cancelToken() {
fs.writeFile(path.join(this.baseDir, 'info'), JSON.stringify({ url: this.patchUrl, receivedBytes: this.receivedBytes, totalBytes: this.totalBytes }), () => {});// 记录一下当前下载信息
this.source.cancel('Operation canceled by the user.');
}
}
export default new StreamDownload();
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…