背景:看到某技术交流群有朋友咨询断点续传是什么,怎么实现,寻思写篇短文简单介绍一下。
在应用开发中,断点续传功能通常指在文件上传或下载过程中,当因网络中断、应用崩溃等原因导致传输中断后,能够从上次中断的位置继续进行传输。以下分别从文件下载和文件上传两个方面介绍实现断点续传的方法:
文件下载断点续传
实现思路
- 获取文件总大小:在开始下载前,通过 HTTP 请求的响应头获取文件的总大小。
- 记录已下载位置:在下载过程中,不断记录已下载的字节数,并将其保存到本地文件或数据库中。
-
恢复下载:当需要恢复下载时,从本地读取已下载的字节数,然后在 HTTP 请求中添加
Range
头,指定从该位置开始继续下载。
代码示例
import http from '@ohos.net.http';
import fs from '@ohos.file.fs';
async function downloadFile(url: string, filePath: string) {
let startByte = 0;
try {
// 检查本地文件是否存在,若存在则获取已下载的字节数
const fileStat = await fs.stat(filePath);
startByte = fileStat.size;
} catch (e) {
// 文件不存在,从头开始下载
}
const httpRequest = http.createHttp();
// 设置请求头,指定从 startByte 位置开始下载
httpRequest.setRequestHeader('Range', `bytes=${startByte}-`);
httpRequest.request(
url,
{
method: http.RequestMethod.GET,
connectTimeout: 60000,
readTimeout: 60000,
},
(err, response) => {
if (err) {
console.error('下载请求出错:', err);
return;
}
const totalSize = parseInt(response.getHeader('Content-Length') || '0', 10) + startByte;
const fileStream = fs.createWriteStream(filePath, { flags: startByte > 0 ? 'a' : 'w' });
response.on('data', (chunk) => {
startByte += chunk.length;
fileStream.write(chunk);
console.log(`已下载: ${startByte}/${totalSize}`);
});
response.on('end', () => {
fileStream.end();
console.log('下载完成');
});
response.on('error', (error) => {
fileStream.end();
console.error('下载过程出错:', error);
});
}
);
}
// 使用示例
const downloadUrl = 'http://example.com/file.zip';
const savePath = '/data/storage/el2/base/haps/entry/files/file.zip';
downloadFile(downloadUrl, savePath);
文件上传断点续传
实现思路
- 记录已上传位置:在上传过程中,记录已上传的字节数,并将其保存到本地文件或数据库中。
- 恢复上传:当需要恢复上传时,从本地读取已上传的字节数,然后从该位置开始继续上传文件内容。
代码示例
import http from '@ohos.net.http';
import fs from '@ohos.file.fs';
async function uploadFile(url: string, filePath: string) {
let uploadedBytes = 0;
try {
// 检查本地记录文件是否存在,若存在则获取已上传的字节数
const recordFile = '/data/storage/el2/base/haps/entry/files/upload_record.txt';
const recordStat = await fs.stat(recordFile);
if (recordStat.size > 0) {
const recordContent = await fs.readFile(recordFile, { encoding: 'utf-8' });
uploadedBytes = parseInt(recordContent, 10);
}
} catch (e) {
// 记录文件不存在,从头开始上传
}
const fileStream = fs.createReadStream(filePath, { start: uploadedBytes });
const httpRequest = http.createHttp();
httpRequest.request(
url,
{
method: http.RequestMethod.POST,
connectTimeout: 60000,
readTimeout: 60000,
extraData: fileStream,
},
(err, response) => {
if (err) {
console.error('上传请求出错:', err);
return;
}
fileStream.on('data', (chunk) => {
uploadedBytes += chunk.length;
// 保存已上传的字节数到本地记录文件
fs.writeFile('/data/storage/el2/base/haps/entry/files/upload_record.txt', uploadedBytes.toString(), { encoding: 'utf-8' });
console.log(`已上传: ${uploadedBytes}`);
});
fileStream.on('end', () => {
// 上传完成后删除记录文件
fs.unlink('/data/storage/el2/base/haps/entry/files/upload_record.txt');
console.log('上传完成');
});
fileStream.on('error', (error) => {
console.error('上传过程出错:', error);
});
}
);
}
// 使用示例
const uploadUrl = 'http://example.com/upload';
const filePath = '/data/storage/el2/base/haps/entry/files/file.zip';
uploadFile(uploadUrl, filePath);
注意事项
-
服务器端支持:无论是文件下载还是上传的断点续传,都需要服务器端支持
Range
请求头。在下载时,服务器应能够根据Range
头返回指定范围的文件内容;在上传时,服务器应能够处理从指定位置开始的文件上传。 - 数据一致性:在记录已下载或已上传的字节数时,要确保数据的一致性,避免因异常情况导致记录的数据不准确。
- 错误处理:在实际开发中,需要对各种可能的错误情况进行处理,如网络错误、文件读写错误等,以提高程序的健壮性。