flutter开发实战-dio文件下载实现

这篇具有很好参考价值的文章主要介绍了flutter开发实战-dio文件下载实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

flutter开发实战-dio文件下载实现

在开发中,需要下载文件,这里使用的是dio
dio 是一个强大的 Dart HTTP 请求库,支持全局配置、Restful API、FormData、拦截器、 请求取消、Cookie 管理、文件上传/下载、超时以及自定义适配器等。

flutter 下载文件,flutter开发实战,flutter,移动开发,flutter,dio,下载

一、引入dio

在工程中pubspec.yaml引入dio

dio: ^5.1.1
  dio_cookie_manager: ^3.0.0

二、代码实现

我们对dio进行封装

// 定义枚举,请求方法枚举
enum HttpApiMethod {
  GET,
  POST,
  DELETE,
  PUT,
}

// 网络请求的成功与失败
// 上传
typedef OnUploaded = void Function(Map<String, dynamic> result);
// 下载进度
typedef OnDownloadProgress = void Function(int count, int total);
// 下载成功
typedef OnDownloaded = void Function();
// 请求成功
typedef OnSuccess = void Function(ResponseData responseData);
// 请求失败
typedef OnFailure = void Function(ApiHttpError error);

// 请求Api
class HttpApi {
  // 网络请求库dio
  Dio dio = Dio(BaseOptions(
    // connectTimeout: 60000, // 连接服务器超时时间,单位是毫秒.
    // receiveTimeout: 10000, // 响应流上前后两次接受到数据的间隔,单位为毫秒, 这并不是接收数据的总时限
    headers: {
      HttpHeaders.acceptHeader: "text/plain,"
          "text/plain,"
          "multipart/form-data,"
          "application/json,"
          "text/html,"
          "image/jpeg,"
          "image/png,"
          "application/octet-stream,"
          "text/json,"
          "text/javascript,"
          "text/html",
    },
  ));

  // 私有构造函数
  HttpApi._internal();

  //保存单例
  static HttpApi _singleton = HttpApi._internal();

  //工厂构造函数
  factory HttpApi() => _singleton;

  /// 配置请求头header
  ///   /// The request Content-Type. The default value is 'application/json; charset=utf-8'.
  //   /// If you want to encode request body with 'application/x-www-form-urlencoded',
  //   /// you can set [Headers.formUrlEncodedContentType], and [Dio]
  //   /// will automatically encode the request body.
  Future<void> configHeaders(
      String requestUrl, Map<String, dynamic>? params) async {

    dio.options.headers['Content-Type'] = Headers.jsonContentType;

    LoggerManager().info(
        "requestUrl:${requestUrl} dio.options.headers:${dio.options.headers}");
  }

  get(String url, ApiServiceDomain serviceDomain,
      {Map<String, dynamic>? params, OnSuccess? success, OnFailure? failure}) {
    doRequest(url, serviceDomain, HttpApiMethod.GET,
        params: params, success: success, failure: failure);
  }

  post(String url, ApiServiceDomain serviceDomain,
      {Map<String, dynamic>? params, OnSuccess? success, OnFailure? failure}) {
    doRequest(url, serviceDomain, HttpApiMethod.POST,
        params: params, success: success, failure: failure);
  }

  // 请求服务器
  // params,参数
  // 请求成功
  // 请求失败
  Future<void> doRequest(
      String url, ApiServiceDomain serviceDomain, HttpApiMethod method,
      {Map<String, dynamic>? params,
      OnSuccess? success,
      OnFailure? failure}) async {
    String requestUrl = getRequestUrl(url, serviceDomain);

    try {
      /// 可以添加header
      await configHeaders(requestUrl, params);
      Response? response;
      switch (method) {
        case HttpApiMethod.GET:
          {
            // get请求
            if (params != null && params.isNotEmpty) {
              response = await dio.get(requestUrl,
                  queryParameters: params,
                  options: Options(contentType: Headers.jsonContentType));
              LoggerManager()
                  .debug("await dio.get response:$response,params:$params");
            } else {
              response = await dio.get(requestUrl,
                  options: Options(contentType: Headers.jsonContentType));
            }
            break;
          }
        case HttpApiMethod.POST:
          {
            // post请求
            String? contentType = Headers.formUrlEncodedContentType;
            if (params != null && params.isNotEmpty) {
              response = await dio.post(requestUrl,
                  data: params, options: Options(contentType: contentType));
              LoggerManager()
                  .debug("await dio.post response:$response,params:$params");
            } else {
              response = await dio.post(requestUrl,
                  options: Options(contentType: contentType));
            }
            break;
          }
        // case HttpApiMethod.PUT: {
        //   break;
        // }
        // case HttpApiMethod.DELETE: {
        //   break;
        // }
        default:
      }
      LoggerManager().debug('doRequest: $response, params:$params');

      if (response != null) {
        Map<String, dynamic> result = json.decode(response.toString());
        assert(() {
          // assert只会在debug模式下执行,release模式下不会执行
          // 打印信息
          LoggerManager().debug('''api: $requestUrl\nresult: $result''');
          return true;
        }());

        ResponseData responseData = ResponseData.fromJson(result);
        if (responseData.status == 0) {
          if (success != null) {
            //返回请求数据
            success(responseData);
          }
        } else {
          //返回失败信息
          ApiHttpError apiHttpError = getErrorRequestResponseData(responseData);

          LoggerManager().debug("apiHttpError:${apiHttpError.toString()}");

          LoggerManager().error('''api: $requestUrl\nresult: $result''');

          if (failure != null) {
            failure(apiHttpError);
          }
        }
      } else {
        // 没有获得response,failure
        ApiHttpError apiHttpError =
            ApiHttpError(ApiHttpErrorType.Default, "请求失败!");

        LoggerManager().debug("apiHttpError:${apiHttpError.toString()}");

        if (failure != null) {
          failure(apiHttpError);
        }
      }
    } on DioError catch (e, s) {
      // catch到异常,failure
      // The request was made and the server responded with a status code
      // that falls out of the range of 2xx and is also not 304.
      LoggerManager()
          .error("doRequest api: $requestUrl, dioError:${e.message}, s:$s");
      ApiHttpError apiHttpError = getRequestFailure(e.response, e.type);

      LoggerManager().debug("apiHttpError:${apiHttpError.toString()}");

      if (failure != null) {
        failure(apiHttpError);
      }
    } catch (e) {
      // 可以捕获任意异常
      ApiHttpError apiHttpError =
          ApiHttpError(ApiHttpErrorType.Default, "${e.toString()}");

      if (failure != null) {
        failure(apiHttpError);
      }
    }
  }

  // 上传文件(图片)
  doUploadFile(String url, UploadFileInfo fileInfo,
      {Map<String, dynamic>? params,
      OnUploaded? uploaded,
      OnFailure? failure}) async {
    try {
      String timeStamp = DateTime.now().millisecondsSinceEpoch.toString();
      Map<String, dynamic> fromParams = Map();
      if (params != null && params.isNotEmpty) {
        fromParams.addAll(params);
      }

      fromParams["file"] = await MultipartFile.fromFile(fileInfo.file.path,
          filename: '${fileInfo.key}-${timeStamp}.jpg');

      FormData formData = FormData.fromMap(fromParams);
      Response? response = await dio.post(url, data: formData);
      assert(() {
        // assert只会在debug模式下执行,release模式下不会执行
        // 打印信息
        LoggerManager().error('''api: $url\nresult: $response''');
        return true;
      }());

      if (response != null) {
        Map<String, dynamic> result = json.decode(response.toString());
        assert(() {
          // assert只会在debug模式下执行,release模式下不会执行
          // 打印信息
          LoggerManager().debug('''api: $url\nresult: $result''');
          return true;
        }());

        if (response.statusCode == 200) {
          if (uploaded != null) {
            uploaded(result);
          }
        } else {
          //返回失败信息
          LoggerManager().error('''api: $url\nresult: $result''');

          ApiHttpError apiHttpError =
              ApiHttpError(ApiHttpErrorType.Default, "请求失败!");

          if (failure != null) {
            failure(apiHttpError);
          }
        }
      } else {
        //返回失败信息
        // 没有获得response,failure
        ApiHttpError apiHttpError =
            ApiHttpError(ApiHttpErrorType.Default, "请求失败!");

        if (failure != null) {
          failure(apiHttpError);
        }
      }
    } on DioError catch (e, s) {
      // catch到异常,failure
      LoggerManager().error("doUploadFile api: $url, dioError:$e, s:$s");
      ApiHttpError apiHttpError = getRequestFailure(e.response, e.type);

      if (failure != null) {
        failure(apiHttpError);
      }
    } catch (e) {
      // 可以捕获任意异常
      ApiHttpError apiHttpError =
          ApiHttpError(ApiHttpErrorType.Default, "${e.toString()}");

      if (failure != null) {
        failure(apiHttpError);
      }
    }
  }

  // 下载文件
  void doDownload(String url, String savePath,
      {required CancelToken cancelToken,
      Map<String, dynamic>? params,
      dynamic? data,
      Options? options,
      OnDownloadProgress? progress,
      OnDownloaded? completion,
      OnFailure? failure}) async {
    try {
      dio.download(
        url,
        savePath,
        queryParameters: params,
        cancelToken: cancelToken,
        onReceiveProgress: (int count, int total) {
          if (total != -1) {
            if (!cancelToken.isCancelled) {
              double downloadRatio = (count / total);
              if (downloadRatio == 1) {
                if (completion != null) {
                  completion();
                }
              } else {
                if (progress != null) {
                  progress(count, total);
                }
              }
            }
          } else {
            ApiHttpError apiHttpError =
                ApiHttpError(ApiHttpErrorType.Default, "无法获取文件大小,下载失败!");

            if (failure != null) {
              failure(apiHttpError);
            }
          }
        },
      );
    } on DioError catch (e) {
      ApiHttpError apiHttpError =
          ApiHttpError(ApiHttpErrorType.Default, e.toString());
      if (CancelToken.isCancel(e)) {
        apiHttpError = ApiHttpError(ApiHttpErrorType.Cancel, "下载已取消!");
      } else {
        if (e.response != null) {
          apiHttpError = getRequestFailure(e.response, e.type);
        } else {
          apiHttpError = ApiHttpError(ApiHttpErrorType.Default, e.message??"");
        }
      }

      if (failure != null) {
        failure(apiHttpError);
      }
    } on Exception catch (e) {
      // EasyLoading.showError(e.toString());
      ApiHttpError apiHttpError =
          ApiHttpError(ApiHttpErrorType.Default, e.toString());

      if (failure != null) {
        failure(apiHttpError);
      }
    } catch (e) {
      // 可以捕获任意异常
      ApiHttpError apiHttpError =
          ApiHttpError(ApiHttpErrorType.Default, "${e.toString()}");

      if (failure != null) {
        failure(apiHttpError);
      }
    }
  }

  // 根据服务器来拼接服务器具体地址
  String getRequestUrl(String url, ApiServiceDomain serviceDomain) {
    String requestUrl = url;
    return requestUrl;
  }

  ApiHttpError getErrorRequestResponseData(ResponseData responseData) {
    //返回失败信息
    ApiHttpError apiHttpError =
        ApiHttpError(ApiHttpErrorType.Default, responseData.errorMsg);

    if (kNeedAuthLoginErrorCode == responseData.errorCode) {
      apiHttpError = ApiHttpError(ApiHttpErrorType.Auth, responseData.errorMsg);
    }

    return apiHttpError;
  }

  ApiHttpError getRequestFailure(
      Response? response, DioErrorType dioErrorType) {
    LoggerManager()
        .error("getRequestFailure: $response, dioError:$dioErrorType");

    ApiHttpErrorType errorType = ApiHttpErrorType.Default;
    String errorMessage = "请求失败!";

    if (response != null) {
      if (dioErrorType == DioErrorType.connectionTimeout) {
        errorType = ApiHttpErrorType.NetWork;
        errorMessage = "网络链接异常!";
      } else if (dioErrorType == DioErrorType.sendTimeout) {
        errorType = ApiHttpErrorType.Timeout;
        errorMessage = "网络链接异常!";
      } else if (dioErrorType == DioErrorType.receiveTimeout) {
        errorType = ApiHttpErrorType.Timeout;
        errorMessage = "网络链接异常!";
      } else if (dioErrorType == DioErrorType.badResponse) {
        // When the server response, but with a incorrect status, such as 404, 503...
        if (response != null) {
          if (response.statusCode == 401) {
            errorType = ApiHttpErrorType.Auth;
            errorMessage = "认证失败!";
          } else if (response.statusCode == 400) {
            errorType = ApiHttpErrorType.BadRequest;
            errorMessage = "无效请求!";
          } else if (response.statusCode == 404) {
            errorType = ApiHttpErrorType.NotFound;
            errorMessage = "访问的资源丢失了!";
          } else if (response.statusCode == 405) {
            // 请求的方法错误
            errorType = ApiHttpErrorType.BadParamHeader;
            errorMessage = "参数出错!";
          } else if (response.statusCode! >= 500) {
            errorType = ApiHttpErrorType.BadRequest;
            errorMessage = "服务器居然累倒了!";
          }
        }
      } else if (dioErrorType == DioErrorType.cancel) {
        errorType = ApiHttpErrorType.Cancel;
        errorMessage = "请求已经取消";
      }
    } else {
      errorType = ApiHttpErrorType.NetWork;
      errorMessage = "网络链接异常!";
    }

    ApiHttpError apiHttpError = ApiHttpError(errorType, errorMessage);
    return apiHttpError;
  }
}

/// 上传的文件类
class UploadFileInfo {
  File file;
  String key;

  UploadFileInfo({required this.file, required this.key});
}

文件下载页面实现实例

class DownloadPage extends StatefulWidget {
  const DownloadPage({
    Key? key,
    this.messages,
    this.uniqueId,
    this.arguments,
  }) : super(key: key);

  final Object? arguments;
  final String? messages;
  final String? uniqueId;

  
  State<DownloadPage> createState() => _DownloadPageState();
}

class _DownloadPageState extends State<DownloadPage> {
  String _downloadPath =
      'https://dl.google.com/chrome/mac/stable/GGRO/googlechrome.dmg';
  double _downloadRatio = 0.0;
  String _downloadIndicator = '0.00%';
  late String _destPath;
  late CancelToken _token;
  bool _downloading = false;

  
  void initState() {
    getTemporaryDirectory()
        .then((tempDir) => {_destPath = tempDir.path + 'googlechrome.dmg'});

    super.initState();
  }

  void _downloadFile() {
    if (_downloading == true) {
      return;
    }
    _token = CancelToken();
    _downloading = true;
    HttpApi().doDownload(_downloadPath, _destPath, cancelToken: _token,
        progress: (int received, int total) {
      // 下载进度
      setState(() {
        _downloadRatio = (received / total);
        if (_downloadRatio == 1) {
          _downloading = false;
        }
        _downloadIndicator = (_downloadRatio * 100).toStringAsFixed(2) + '%';
      });
    }, completion: () {
      // 下载成功
      _downloading = false;
      FlutterLoadingHud.showToast(message: "\"下载完成\"");
    }, failure: (error) {
      // 下载出错
      _downloading = false;
      FlutterLoadingHud.showToast(message: error.message);
    });
  }

  void _cancelDownload() {
    if (_downloadRatio < 1.0) {
      _token.cancel();
      _downloading = false;
      setState(() {
        _downloadRatio = 0;
        _downloadIndicator = '0.00%';
      });
    }
  }

  void _deleteFile() {
    try {
      File downloadedFile = File(_destPath);
      if (downloadedFile.existsSync()) {
        downloadedFile.delete();
      } else {
        FlutterLoadingHud.showToast(message: "文件不存在");
      }
    } catch (e) {
      FlutterLoadingHud.showToast(
          message: "${e.toString()}");
    }
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      resizeToAvoidBottomInset: false,
      appBar: AppBar(
        leading: AppBarIconButton(
          icon: Icon(Icons.arrow_back_ios),
          onPressed: () => {NavigatorRoute.pop()},
        ),
        centerTitle: true,
        backgroundColor: ColorUtil.hexColor(0xffffff),
        foregroundColor: ColorUtil.hexColor(0x777777),
        elevation: 0,
        title: Text(
          "下载示例",
          textAlign: TextAlign.center,
          softWrap: true,
          style: TextStyle(
            fontSize: 17,
            color: ColorUtil.hexColor(0x333333),
            fontWeight: FontWeight.w600,
            fontStyle: FontStyle.normal,
            decoration: TextDecoration.none,
          ),
        ),
        shadowColor: ColorUtil.hexColor(0xffffff),
        toolbarHeight: 44.0,
        bottomOpacity: 0.0,
      ),
      body: Container(
        color: ColorUtil.hexColor(0xf7f7f7),
        alignment: Alignment.center,
        padding: EdgeInsets.all(10),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Row(
              children: [
                _buildDownloadButton(),
                TextButton(
                  child: Text('取消'),
                  onPressed: () {
                    _cancelDownload();
                  },
                ),
                TextButton(
                  child: Text(
                    '删除',
                    style: TextStyle(
                        color: !_downloading ? Colors.red : Colors.grey),
                  ),
                  onPressed: (!_downloading ? _deleteFile : null),
                  style: ButtonStyle(),
                ),
              ],
            ),
            SizedBox(
              height: 25,
            ),
            Row(children: [
              Expanded(
                child: LinearProgressIndicator(
                  backgroundColor: Colors.grey[600],
                  value: _downloadRatio,
                ),
              ),
              SizedBox(
                width: 5,
              ),
              Text(
                _downloadIndicator,
                style: TextStyle(color: Colors.black, fontSize: 12.0),
              ),
            ]),
          ],
        ),
      ),
    );
  }

  Widget _buildDownloadButton() {
    return ButtonWidget(
      onPressed: () {
        _downloadFile();
      },
      child: Text(
        "下载文件",
        textAlign: TextAlign.center,
        softWrap: true,
        style: TextStyle(
          fontSize: 16,
          fontWeight: FontWeight.w400,
          fontStyle: FontStyle.normal,
          color: ColorUtil.hexColor(0xffffff),
          decoration: TextDecoration.none,
        ),
      ),
      height: 40,
      width: 100.0,
      highlightedColor: ColorUtil.hexColor(0xff462e),
      bgColor: ColorUtil.hexColor(0xff462e),
      bgHighlightedColor: ColorUtil.hexColor(0xff462e, alpha: 0.75),
      enabled: true,
      bgDisableColor: Colors.grey,
      borderRadius: 22.0,
    );
  }
}

三、小结

flutter开发实战-dio文件下载实现,封装dio下载功能,实现文件下载

学习记录,每天不停进步。文章来源地址https://www.toymoban.com/news/detail-675951.html

到了这里,关于flutter开发实战-dio文件下载实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Flutter:文件上传与下载

    dio是一个强大的Dart Http请求库,提供了丰富的功能和易于使用的API,支持文件上传和下载。 这个就不介绍了,网上有很多的封装案例。 简介 适用于iOS,Android,MacOS,Windows和Linux的后台文件下载器和上传器。 官方文档 https://pub-web.flutter-io.cn/packages/background_downloader 安装 注意:

    2024年02月13日
    浏览(61)
  • flutter 文件下载及存储路径

    日常开发中,经常会遇到下载文件的功能,往往我们在需要保存文件的路径上去调试,比如Android中的路径,有些会报错在SD卡中,但是有些手机,又没有SD卡,那么我们该怎么办呢? 为了功能的完善,往往需要一个下载的进度条,当然,UI怎么美化不管,核心代码如下: 上面

    2024年02月01日
    浏览(43)
  • node实战——koa实现文件下载和图片/pdf/视频预览(node后端储备知识)

    大家好,我是yma16,本文分享关于node实战——koa实现文件下载和图片预览。 本文适用对象 :前端初学者转node方向,在校大学生,即将毕业的同学,计算机爱好者。 node系列往期文章 node_windows环境变量配置 node_npm发布包 linux_配置node node_nvm安装配置 node笔记_http服务搭建(渲染

    2024年02月05日
    浏览(39)
  • 完全离线环境下安装配置Vscode Python开发环境及离线包文件的下载与安装实战

    一般在进行项目开发时,都是在有网络的环境下进行的,此时的开发工作,由于有网络的支持,我们需要什么安装包、模块、或者工具,下载安装比较方便,缺少依赖环境时,有些系统和安装包会自动下载补齐。但在一些特情况境下,需要配置完全独立于网络的开发环境,这

    2024年02月04日
    浏览(70)
  • 【下载文件】uniapp开发小程序,下载文件并保存到本地

    1.1实现效果:点击文件附件,下载到本地 1.2具体代码: 2.1:效果图 保存方式是:点击下载按钮,通过微信选择一个好友,发给给好友的方式,进行保存。 2.2实现代码: 下载按钮: js:

    2024年02月16日
    浏览(52)
  • flutter开发实战-应用更新apk下载、安装apk、启动应用实现

    flutter开发实战-应用更新apk下载、安装apk、启动应用实现 在开发过程中,经常遇到需要更新下载新版本的apk文件,之后进行应用更新apk下载、安装apk、启动应用。我们在flutter工程中实现下载apk,判断当前版本与需要更新安装的版本进行比对判断,通过判断VersionCode来确定下载

    2024年02月02日
    浏览(57)
  • Vue实战【后端返回文件流时,前端如何处理并成功下载流文件】

    哈喽小伙伴们,在我们日常工作当中,大家一定会遇到 文件导出 这样的功能需求点;导出功能前端后端都可以实现,通常情况下呢是由我们后端同事去处理的,那么当我们后端同事给你返回文件流的时候,前端要怎么处理并且完成下载呢?今天就给大家说个简单的方法,我

    2024年02月11日
    浏览(42)
  • 【springboot项目开发】文件上传与下载

    目录 总体介绍 文件上传 介绍 文件上传的前端需求 文件上传的前端代码 文件上传的后端需求 文件上传的后端代码 文件下载 介绍 前端需求 前端代码 后端需求 后端代码 文件的上传和下载功能,是项目开发过程中比较常见的业务需求,我们在客户端被展现的视觉效果通过如

    2024年02月08日
    浏览(54)
  • 【Java 实现文件下载】vue前端+java后端实现文件下载详解(附源码)

    【 写在前面 】前端时间总结了一下有关java文件上传的功能,也给不少读者带来一些帮助,因此今天继续完善文件下载这套体系,希望能给屏幕前的您带来实质性的帮助,其实文件下载最怕的就是中文乱码的现象,当然这个我单独写了一篇文章解释,这里不做详谈。 涉及知识

    2024年02月09日
    浏览(56)
  • 前端实现文件下载功能——文件流

    前端下载文件一般使用的是blob 核心的步骤是获取后端响应的文件流,用blob创建一个临时的URL,然后创建一个隐藏的a标签,实现下载需求。 那就先上代码 如果后端响应的数据是一个二进制数据,那我们就得这是响应类型是blob,否则浏览器会默认按照json解析 至于后端如何向

    2024年02月11日
    浏览(52)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包