flutter基于dio实现断点续传下载

flutter基于dio实现断点续传下载,第1张

由于flutter断点下载文章不怎么多也没有几个案例,找了半天还是在dio案例上找到的花了些冤枉时间。
基于 dio: ^4.0.0path_provider: ^2.0.2实现一个下载大文件的功能
具体下载逻辑尚未完善只是提供一个思路 也参考了:https://blog.csdn.net/qin19930929/article/details/94628973

核心代码
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'package:dio/dio.dart';
import 'package:path_provider/path_provider.dart';

class DownloadFile {
  /// 用于记录正在下载的url,避免重复下载
  static var downloadingUrls = Map<String, CancelToken>();

  /// 断点下载大文件
  static Future<void> download({
    required String url,
    required String savePath,
    ProgressCallback? onReceiveProgress,
    void Function()? done,
    void Function(DioError)? failed,
  }) async {
    int downloadStart = 0;
    bool fileExists = false;
    File f = File(savePath);
    if (await f.exists()) {
      downloadStart = f.lengthSync();
      fileExists = true;
    }
    print("开始:$downloadStart");
    if (fileExists && downloadingUrls.containsKey(url)) {
      return;
    }
    var dio = Dio();
    CancelToken cancelToken = CancelToken();
    downloadingUrls[url] = cancelToken;
    try {
      var response = await dio.get<ResponseBody>(
        url,
        options: Options(
          /// 以流的方式接收响应数据
          responseType: ResponseType.stream,
          followRedirects: false,
          headers: {
            /// 分段下载重点位置
            "range": "bytes=$downloadStart-",
          },
        ),
      );
      File file = File(savePath);
      RandomAccessFile raf = file.openSync(mode: FileMode.append);
      int received = downloadStart;
      int total = await _getContentLength(response);
      Stream<Uint8List> stream = response.data!.stream;
      StreamSubscription<Uint8List>? subscription;
      subscription = stream.listen(
        (data) {
          /// 写入文件必须同步
          raf.writeFromSync(data);
          received += data.length;
          onReceiveProgress?.call(received, total);
        },
        onDone: () async {
          downloadingUrls.remove(url);
          await raf.close();
          done?.call();
        },
        onError: (e) async {
          await raf.close();
          downloadingUrls.remove(url);
          failed?.call(e as DioError);
        },
        cancelOnError: true,
      );
      cancelToken.whenCancel.then((_) async {
        await subscription?.cancel();
        await raf.close();
      });
    } on DioError catch (error) {
      /// 请求已发出,服务器用状态代码响应它不在200的范围内
      if (CancelToken.isCancel(error)) {
        print("下载取消");
      } else {
        failed?.call(error);
      }
      downloadingUrls.remove(url);
    }
  }

  /// 获取下载的文件大小
  static Future<int> _getContentLength(Response<ResponseBody> response) async {
    try {
      var headerContent =
          response.headers.value(HttpHeaders.contentRangeHeader);
      print("下载文件$headerContent");
      if (headerContent != null) {
        return int.parse(headerContent.split('/').last);
      } else {
        return 0;
      }
    } catch (e) {
      return 0;
    }
  }

  /// 取消任务
  static void cancelDownload(String url) {
    downloadingUrls[url]?.cancel();
    downloadingUrls.remove(url);
  }
}
调用案例
void main() {
  runApp(TestMyApp());
}

class TestMyApp extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _TestMyAppState();
  }
}

class _TestMyAppState extends State<TestMyApp> {
  void download() async {
    var url = "mp4";
    Directory dir = await getApplicationDocumentsDirectory();
    var sDCardDir = dir.path;
    var savePath = sDCardDir + "/video/1.mp4";
    File f = File(sDCardDir + "/video");
    if (!await f.exists()) {
      new Directory(sDCardDir + "/video").createSync();
    }

    await DownLoadManage().download(
      url: url,
      savePath: savePath,
      onReceiveProgress: (received, total) {
        if (total != -1) {
          print("下载1已接收:" +
              received.toString() +
              "总共:" +
              total.toString() +
              "进度:+${(received / total * 100).floor()}%");
        }
      },
      done: () {
        print("下载1完成");
      },
      failed: (e) {
        print("下载1失败:" + e.toString());
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.white,
      child: Center(
        child: GestureDetector(
          onTap: () {
            download();
          },
          child: Container(
            width: 150,
            height: 150,
            color: Colors.red,
          ),
        ),
      ),
    );
  }
}

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/web/997014.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-21
下一篇 2022-05-21

发表评论

登录后才能评论

评论列表(0条)

保存