Flutter笔记:关于Flutter中的大文件上传(上)

Flutter笔记
关于Flutter中的大文件上传(上)
大文件上传背景与 Flutter 端实现文件分片传输

作者李俊才 (jcLee95):https://blog.csdn.net/qq_28550263
邮箱 :291148484@163.com
本文地址:https://blog.csdn.net/qq_28550263/article/details/134302751


本系列上下两篇文章,包括 Flutter 端和 Django 端(后端)两个部分,讨论在 Flutter 端如何处理大文件,以及在 Django 端如何接收。


1. 概述

1.1 大文件上传的应用场景

在Flutter应用中,大文件上传的场景非常常见。例如,用户可能需要上传高清照片、视频或其他大型媒体文件。在企业应用中,用户可能需要上传大量的数据文件或报告。本文的主题是在Flutter应用中实现大文件上传。我们将探讨大文件上传的常见问题,介绍一些有效的上传策略,并提供实现这些策略的Flutter代码示例。我们的目标是帮助读者理解如何在Flutter应用中有效地上传大文件。

1.2 大文件上传的挑战

大文件上传面临许多挑战。首先,网络不稳定可能导致上传失败或速度慢。其次,由于文件大小,上传可能需要很长时间,而上传进度可能不明确,导致用户体验不佳。此外,如果应用在上传过程中崩溃或被关闭,可能需要从头开始上传,这会浪费大量时间和带宽。

1.3 整体上传局限性与优化策略

传统的文件上传方法通常是将整个文件作为一个请求体发送到服务器。然而,这种方法对大文件来说并不理想。由于网络不稳定,大文件有更大的可能性在上传过程中失败。此外,如果应用在上传过程中崩溃或被关闭,可能需要从头开始上传。

为了解决这些问题,我们可以采用一些策略来优化大文件的上传。

1.3.1 分块上传

例如,我们可以使用分块上传,将文件分成多个小块,然后分别上传。这样,即使某个块上传失败,也只需要重新上传该块,而不是整个文件。

1.3.2 断点续传

另一种策略是断点续传,它允许我们在上传被中断后从中断点继续上传,而不是从头开始。这两种策略各有优缺点。分块上传可以提高上传的成功率,但可能需要更复杂的服务器支持。断点续传可以节省带宽,但需要服务器支持范围请求,并且需要在客户端保存上传的进度。

2. 在Flutter中实现大文件上传前的准备

在这一章节中,我们将详细介绍如何在Flutter中实现大文件上传。我们将通过以下步骤来实现这个过程:获取权限、选择文件、分块、上传和处理错误。

2.1 获取权限

在用户选择文件之前,我们需要确保应用有读取文件的权限。我们可以使用permission_handler插件来请求权限。首先,需要在pubspec.yaml文件中添加permission_handler依赖以获取权限。

dependencies:
  permission_handler: 替换你安装的版本号

或者直接在项目中运行下面的命令安装最新版:

flutter pub add permission_handler

然后,我们可以使用Permission类的request方法来请求权限:

import 'package:permission_handler/permission_handler.dart';

void requestPermission() async {
  var status = await Permission.storage.status;
  if (!status.isGranted) {
    status = await Permission.storage.request();
  }
}

2.2 文件选择

在获取了权限后,我们可以使用file_picker插件来选择文件。首先,需要在pubspec.yaml文件中添加依赖:

dependencies:
  file_picker: 替换你安装的版本号

或者直接在项目中运行下面的命令安装最新版:

flutter pub add file_picker

然后,可以使用FilePicker的getFiles方法来选择文件:

import 'package:file_picker/file_picker.dart';

void pickFile() async {
  FilePickerResult? result = await FilePicker.platform.pickFiles();

  if(result != null) {
    PlatformFile file = result.files.first;
    print(file.name);
    print(file.bytes);
    print(file.size);
    print(file.extension);
    print(file.path);
  } else {
    // User canceled the picker
  }
}

3. 文件的切片

3.1 基本思路

在选择了文件后,我们需要将文件分块。分块的目的是将大文件切分成多个小块,这样我们可以逐个上传这些小块,而不是一次性上传整个大文件。这种方法有两个优点:

  1. 如果某个块上传失败,我们只需要重新上传该块,而不是整个文件;
  2. 我们可以更精确地追踪上传进度,因为我们可以知道已经上传了多少块和还剩下多少块。

3.2 实现步骤

在Flutter中,我们可以通过以下步骤来实现文件分块:

  1. 首先,我们需要 获取文件的字节数据
    我们可以使用 PlatformFile 对象的 bytes 属性来获取这些数据。这将返回一个 Uint8List 对象,它是一个包含文件所有字节的列表。

  2. 然后,我们需要 创建一个新的列表来存储分块的数据
    我们可以创建一个空的 List<int> 对象来存储这些数据。

  3. 接下来,我们需要 遍历文件的字节数据,并将数据分块
    我们可以使用一个循环来遍历字节数据。在每次迭代中,我们都会取出一块数据,并将这块数据添加到分块数据的列表中。

  4. 最后,返回分块数据的列表。

3.3 示范案例

一个具体的实现代码如下:

List<int> splitFile(PlatformFile file, int chunkSize) {
  List<int> chunks = [];
  List<int> bytes = file.bytes!.toList();
  for (int i = 0; i < bytes.length; i += chunkSize) {
    int end = (i + chunkSize < bytes.length) ? i + chunkSize : bytes.length;
    chunks.add(bytes.sublist(i, end));
  }
  return chunks;
}

其中,chunkSize 参数表示每个块的大小。

bytes.sublist(i, end) 方法用于获取从 iend 的子列表,这就是我们的一个块。

然后,将这个块添加到 chunks 列表中。这个过程会一直重复,直到所有的字节数据都被分块。

4. 切片的上传

在分块后,我们可以逐个上传这些块。在这个过程中,我们需要创建一个 HTTP 请求,将每个块作为请求的一部分,然后发送这个请求到服务器。我们可以使用 http库 来创建和发送 HTTP 请求。

以下是实现这个功能的步骤:

  1. 首先,我们需要创建一个 MultipartRequest 对象。
    这个对象表示一个 multipart/form-data 请求,它可以包含多个文件和其他类型的数据。我们需要指定请求的方法(在这个例子中是 POST )和 URL

  2. 然后,我们需要将块添加到请求中。
    我们可以使用 MultipartFile 类的 fromBytes 方法来创建一个文件对象,然后将这个对象添加到请求的 files 列表中。

  3. 接下来,我们需要发送请求。
    我们可以使用 send 方法来发送请求,这个方法会返回一个 StreamedResponse 对象。

  4. 最后,我们需要检查响应的状态码,以确定上传是否成功。
    如果状态码是 200,那么上传成功;否则,上传失败。

例如:

import 'package:http/http.dart' as http;

void uploadChunk(List<int> chunk, String url) async {
  var request = http.MultipartRequest('POST', Uri.parse(url));
  request.files.add(http.MultipartFile.fromBytes('file', chunk));
  var response = await request.send();
  if (response.statusCode == 200) {
    print('Uploaded!');
  } else {
    print('Failed to upload.');
  }
}

其中:
chunk 参数是我们要上传的块,url 参数是服务器的 URL
MultipartFile.fromBytes 方法的第一个参数是文件的名字,第二个参数是文件的字节数据;
send 方法会异步发送请求,所以我们需要使用 await 关键字来等待它完成。

5. 完整实现

作为Flutter端完整实现我简单写了一下,上传到了 Git Hub 上,并发布在 Pub.dev,地址为:https://pub.dev/packages/slivers_uploader。本文中各个交接讲解的仅仅是思路,而在该实现的版本中,做了诸多必要的调整。我将文件选择和上传分开。
一个Flutter端示例如下:

import 'package:flutter/material.dart';
import 'package:slivers_uploader/slivers_uploader.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'File Uploader Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  final uploader = FileUploader(
    chunkSize: 50 * 1024 * 1024, // 指定分块大小,默认 1024*1024
  );

  MyHomePage({super.key});

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('File Uploader Example'),
      ),
      body: Center(
        child: TextButton(
          child: const Text('Upload File'),
          onPressed: () async {
            var file = await uploader.pickFile();
            await file.upload(
              url: 'http://192.168.31.239:8001/upload/',
              onSuccess: (chunkNumber) {
                ScaffoldMessenger.of(context).showSnackBar(
                  SnackBar(
                      content: Text('Chunk $chunkNumber upload successful!')),
                );
              },
              onError: (chunkNumber) {
                ScaffoldMessenger.of(context).showSnackBar(
                  SnackBar(content: Text('Chunk $chunkNumber upload failed.')),
                );
              },
            );
          },
        ),
      ),
    );
  }
}


大概效果看起来是这样的:

在这里插入图片描述
在后端对应的可以看到通过POST发送过来的提交记录,就像我的后端这样:

在这里插入图片描述

下一篇文章,我们主要介绍 Django 后端如何实现对应的分块上传的文件。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/125805.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

开发知识点-Pygame

Pygame Pygame最小开发框架与最小游戏游戏开发入门单元开篇 Pygame简介安装游戏开发入门语言开发工具的选择 Pygame最小开发框架与最小游戏 游戏开发入门单元开篇 Pygame简介安装 游戏开发入门语言开发工具的选择

【案例卡】clickhouse:多行数据拼接在一行

一、需求 针对clickhouse数据库中&#xff0c;group by 分组后的字符串字段&#xff0c;拼接处理在一行的问题实现。在mysql中&#xff0c;可以用group_concat()函数来实现&#xff0c;而clickhouse数据库不支持此函数&#xff0c;特此记录实现方式。 二、clickhouse相关函数…

FreeRTOS_内存管理

目录 1. 内存管理简介 2. 内存碎片 3. heap_1 内存分配方法 3.1 分配方法简介 4. heap_2 内存分配方法 4.1 分配方法简介 4.2 内存块详解 5. heap_4 内存分配方法 6. FreeRTOS 内存管理实验 6.1 实验程序 内存管理是一个系统基本组成部分&#xff0c;FreeRTOS 中大量…

【刚体姿态运动学】角速度和欧拉角速率的换算关系的详细推导

0 引言 本文以一种新的角度推导刚体姿态运动学&#xff0c;也即角速度和欧拉角速率之间的换算&#xff0c;不同于相似博文的地方在于&#xff0c;本文旨在从原理上给出直观清晰生动的解释。将详细过程记录于此&#xff0c;便于后续学习科研查找需要。 1 符号 符号含义 { E }…

STM32 GPIO

STM32 GPIO GPIO简介 GPIO&#xff08;General Purpose Input Output&#xff09;通用输入输出口&#xff0c;也就是我们俗称的IO口 根据使用场景&#xff0c;可配置为8种输入输出模式 引脚电平&#xff1a;0V~3.3V&#xff0c;部分引脚可容忍5V 数据0就是低电平&#xff0c…

一篇带你精通php

华子目录 什么是phpphp发展史平台支持和数据库支持网站静态网站和动态网站的区别静态网站动态网站的特点 关键名词解析服务器概念IP的概念域名DNS端口 web程序的访问流程静态网站访问流程动态网站访问流程 php标记脚本标记标准标记&#xff08;常用&#xff09; php注释 什么是…

Linux Hadoop平台伪分布式安装

Linux Hadoop 伪分布式安装 1. JDK2. Hadoop3. MysqlHive3.1 Mysql8安装3.2 Hive安装 4. Spark4.1 Maven安装4.2 Scala安装4.3 Spark编译并安装 5. Zookeeper6. HBase 版本概要&#xff1a; jdk&#xff1a; jdk-8u391-linux-x64.tar.gzhadoop&#xff1a;hadoop-3.3.1.tar.gzh…

Spring Ioc 容器启动流程

Spring容器的启动流程 本文基于 Spring 5.3.23 基于XML文件 public void test() {ApplicationContext applicationContext new ClassPathXmlApplicationContext("applicationContext.xml");User user applicationContext.getBean("user", User.class)…

MySQL大表数据导入到MongoDB

修改参数 &#xff0c;开启into outfile的功能 secure_file_priv/home/backups/mysql_outfile 重启数据库是参数生效 按条件导出MySQL数据 select * from receipt_receive_log where gmt_create > 2020-04-13 00:00:00 and gmt_create< 2020-07-13 00:00:00 INTO O…

微信小程序真机调试连接状态一直在正常和未链接之间反复横跳?

背景&#xff1a;小程序真机调试的时候&#xff0c;发现真机的network不显示接口调用情况&#xff0c;控制台也没有输出内容。具体如下所示&#xff1b; 解决方法&#xff1a; 1、确保手机端连接的网络和微信开发者工具网络一致&#xff0c;比如用同一个WiFi 2、真机自动调试…

极狐GitLab CI 助力 .Net 项目研发效率和质量双提升

目录 .NET nuget 自动生成测试包&#xff08;prerelease&#xff09;版本号 .NET 版本号规范 持续集成自动打包 持续集成自动修改版本号 .NET 行级增量代码规范——拯救老项目 本地全量代码规范 行级增量代码规范 很多团队或开发者都会使用 C#、VB 等语言开发 .Net 应用…

ROS源码安装应用,VSCode

ROS源码安装应用 安装一下VSCode 前置文章 到安装程序的目录中: 完成克隆 编译 catkin_make打开ros核心 roscore打开应用程序 rosrun turtlesim turtlesim_node安装一下VSCode deb下载地址 sudo dpkg -i code_1.84.1-1699275408_amd64.deb添加项目工程到工作空间&#xff…

MyBatis中文网

MyBatis中文网https://mybatis.net.cn/ MyBatis 是一款优秀的持久层框架&#xff0c;它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Ja…

go语法入门2

字符串 使用双引号或反引号引起来的任意个字符。它是字面常量。 func main() {var a "abc\n测试" // \n换行fmt.Println(a) } abc 测试func main() {var a "abc\n\t测试" \\换行后在tabfmt.Println(a) } abc测试func main() {var a abc测试 …

MySQL binlog 日志解析后的exec_time导致表示什么时间?

1. exec_time 到底表示什么时间&#xff1f; MySQL binlog日志解析后&#xff0c;我们能看到会有 exec_time &#xff0c;从字面意思理解这个记录的是执行时间&#xff0c;那这个记录的到底是单条sql的执行时间&#xff1f;还是事务的执行时间&#xff1f;下面通过测试来解读一…

ruoyi前后端分离版本开发框架解读---让你快速入门

后端结构 com.ruoyi ├── common // 工具类 │ └── annotation // 自定义注解 │ └── config // 全局配置 │ └── constant // 通用常量 │ └── core …

AcWing99. 激光炸弹

题目 地图上有 N N N 个目标&#xff0c;用整数 X i , Y i X_i,Y_i Xi​,Yi​ 表示目标在地图上的位置&#xff0c;每个目标都有一个价值 W i W_i Wi​。 注意&#xff1a;不同目标可能在同一位置。 现在有一种新型的激光炸弹&#xff0c;可以摧毁一个包含 R R RR RR 个…

“Git 在团队协作中的优化实践“

文章目录 引言&#xff1a;一、Git 简介1.1 Git 基本概念1.2 Git 原理与工作流程 二、 Git 与 SVN 的区别三、Git 的常用命令及操作四、Git 的理论知识&#xff1a;总结&#xff1a; 引言&#xff1a; 随着技术的不断演进和团队的不断发展&#xff0c;代码管理变得越来越重要。…

不用流氓软件,如何在户外使用手机听下载到家中电脑里的音乐文件呢?

文章目录 本教程解决的问题是&#xff1a;按照本教程方法操作后&#xff0c;达到的效果是本教程使用环境&#xff1a;1 群晖系统安装audiostation套件2 下载移动端app 很多老铁想在上班路上听点喜欢的歌或者相声解解闷儿&#xff0c;于是打开手机上的某雅软件和某音乐软件点进去…

2.docker镜像的导入导出

目录 概述docker 常用命令下载导出导入镜像结束 概述 docker 常用命令 本章节使用到的命令&#xff0c;总结在此&#xff0c;后面有使用案例。 命令作用docker images显示镜像docker rmi $(docker images -q)删除系统上所有的镜像docker rmi -f强制删除多个镜像 &#xff1a…