分块下载的最终速度受设备所在网络带宽、源出口速度、每个块大小、以及分块的数量等诸多因素影响,实际过程中很难保证速度最优。在实际开发中,可以先测试对比后再决定是否使用
此处使用Dio的download方法
import 'dart:io';
import 'package:dio/dio.dart';
class DioDownDataClass
{
/* *
* Des: 分段下载
* url:数据、资源的网络地址
* savePath:下载下来的数据保存的位置
* onReceivedProgress: 下载进度回调函数
* */
Future downloadWithChunks(url, savePath, {ProgressCallback onReceivedProgress})
async {
//第一次下载数据的长度 byte
const firstChunkSize = 102;
//最多几次能全部下完
const maxChunk = 3;
//下载的数据总长度
int total = 0;
//
var dio = Dio();
var progress = <int>[];
createCallback(no) {
return (int received, _) {
progress[no] = received;
if (onReceivedProgress != null && total != 0) {
onReceivedProgress(progress.reduce((a, b) => a+b), total);
}
};
}
////start 代表当前块的起始位置,end代表结束位置
//nomber 代表当前是第几块
Future<Response> downloadChunk(url, start, end, nomber) {
progress.add(0);
--end;
return dio.download(
url,
savePath + "temp$nomber",
onReceiveProgress: createCallback(nomber),
options: Options(
//指定请求的内容长度区间
headers: {"range": "byte=$start-$end"},
)
);
}
//合并文件
Future mergeTempFiles(chunk) async {
File f = File(savePath+"temp0");
IOSink ioSink = f.openWrite(mode: FileMode.writeOnlyAppend);
//合并临时文件
for (int i = 1; i < chunk; i ++) {
File _f = File(savePath + "temp$i") ;
await ioSink.addStream(_f.openRead());
await _f.delete();
}
await ioSink.close();
await f.rename(savePath);
}
Response response = await downloadChunk(url, 0 ,firstChunkSize, 0);
if (response.statusCode == 206) { //支持分片下载
//总长度
total = int.parse(response.headers.value(HttpHeaders.contentRangeHeader).split("/").last);
int reserved = total - int.parse(response.headers.value(HttpHeaders.contentLengthHeader));
int chunk = (reserved / firstChunkSize).ceil() + 1;//分成多少段
if ( chunk > 1) {
int chunkSize = firstChunkSize;
if (chunk > maxChunk + 1) {
//如果按每次firtstChunkSize需要下载的次数chunk大于自定义最大次数maxChunk+1时
chunk = maxChunk + 1;
//不在以每次firstChunkSize下载而是按 次数maxChunk大小chunkSize进行下载
chunkSize = (reserved / maxChunk).ceil();
}
var futures = <Future>[];
for (var i=0 ;i < maxChunk; i++) {
//每一次下载的起始位置
int start = firstChunkSize + i * chunkSize;
//开始下载文件
futures.add(downloadChunk(url, start, start+chunkSize, i+1));
}
//等待所有分块全部下载完成
await Future.wait(futures);
}
//合并文件 chunk 是分成的块数 futures 元素个数
await mergeTempFiles(chunk);
}
}
}
分块下载有一个比较使用的场景就是断点续传,可以将文件分为若干个块,然后维护一个下载状态文件用以记录每一个块的状态,这样即使在网络中断后,也可以恢复中断前的状态,具体实现可以自己尝试一下
网友评论