Flutter资源下载、解压及存储数据库(配合getx使用)

前言

在做移动端项目时,一些比较大的文档需要我们缓存到本地之后再进行使用,一般都会涉及到文件下载,大文件更是会将文件打成压缩包下载,在应用程序使用时解压缩,之后再使用,我们的社交项目更是如此,我们有大量的特效资源和装扮资源需要下载使用,因此我们在进行项目的正式开发前,先对文件下载链路做了分析,最终确定使用dio(网络下载)+sembast(非关系型数据库)+archive(解压文件)的链路进行,下面通过一个简单的ToDoList看一下效果

效果

步骤拆解

1. 下载资源

1.1. 创建数据列表

php 复制代码
// 创建初始化的数据列表
List<DownloadItemModel> downloadFileList = [
    DownloadItemModel(
        id: 1,
        name: "图片1",
        path: 'https://miaohu-oss-cdn.miaohu123.com/course/1.zip',
        isDownload: false,
        progress: 0.0,
        savePath: '',
        zipFiles: []),
    DownloadItemModel(
        id: 2,
        name: "图片2",
        path: 'https://miaohu-oss-cdn.miaohu123.com/course/2.zip',
        isDownload: false,
        progress: 0.0,
        savePath: '',
        zipFiles: []),
    DownloadItemModel(
        id: 3,
        name: "svga",
        path: 'https://miaohu-oss-cdn.miaohu123.com/course/3.zip',
        isDownload: false,
        progress: 0.0,
        savePath: '',
        zipFiles: []),
  ];

1.2. 下载文件

下载文件主要是使用dio将网络文件下载到本地,基本目前的安卓和苹果下载到软件所存在的文件中都不需要申请额外的存储权限了(具体情况需要不同设备测试),因此将文件下载到手机中直接监听下载进度即可

ini 复制代码
// 下载未下载的文件
Future onclickDownloadFile() async {
    // 先检查当前的数据有没有下载
    List<DownloadItemModel> filterList =
        downloadFileList.where((element) => !element.isDownload).toList();
    if (filterList.isNotEmpty) {
      // 准备下载
      for (var item in filterList) {
        downloadFile(item);
      }
    }
  }

// 下载文件
Future downloadFile(DownloadItemModel data) async {
  var dir = await localfileDir();
  String path = dir.path;

  // 获取文件类型,也就是最后的后缀
  List<String> urlList = data.path.split('.');
  String fileType = urlList[urlList.length - 1];
  String filePath = "$path/download/${data.id}.$fileType";
  Dio dio = Dio();
  try {
    dio.download(data.path, filePath, onReceiveProgress: (count, total) {
      downloadProgressCallBack(count, total, data.id.toString(), filePath);
    });
  } on DioError catch (e) {
    print(e);
  }
}

// 下载进度监听
downloadProgressCallBack(
  count,
  total,
  String id,
  String filePath,
) async {
  // 修改进度条
  downloadFileList
      .firstWhere((element) => element.id == int.parse(id))
      .progress = count / total;
  update(["download"]);
  if (count == total) {
    downloadFileList
        .firstWhere((element) => element.id == int.parse(id))
        .isDownload = true;
    update(["download"]);
    if (filePath.endsWith('zip')) {
      unzipFiles(filePath, id);
    }
  }
}

2. 解压缩

文件下载完成之后解压,这里注意如果是苹果电脑的压缩文件中可能含有MACOSX文件夹,因此需要对此文件夹忽略处理,当然解决方案有很多,这里就不赘述

ini 复制代码
 // 解压文件
Future unzipFiles(url, String id) async {
  // 获取到文件路径
  var dir = await localfileDir();
  String documentPath = dir.path;
  // 拿到文件的存储路径
  String savePath = "$documentPath/download";

  // 从磁盘读取zip文件
  List<int> bytes = File(url).readAsBytesSync();

  // 解码zip文件
  try {
    Archive archive = ZipDecoder().decodeBytes(bytes);
    String res = '';
    List<String> pathFiles = [];
    // 将解码的文件解压缩到磁盘
    for (ArchiveFile file in archive) {
      if (file.isFile) {
        List<int> tempData = file.content;
        File f = File("$savePath/${file.name}")
          ..createSync(recursive: true)
          ..writeAsBytesSync(tempData);
        if (!f.path.contains('MACOSX')) {
          String clipFile = f.path.substring(dir.path.length);

          // myLog(f.path);
          pathFiles.add(clipFile);
          res = clipFile;
        }
      } else {
        Directory("$savePath/${file.name}").create(recursive: true);
      }
    }
    //整理数据, 通知数据库存储
    List<DownloadItemModel> filterList = downloadFileList
        .where((element) => element.id.toString() == id)
        .toList();
    if (filterList.isNotEmpty) {
      DownloadItemModel data = filterList.first;
      data.savePath = res;
      data.zipFiles = pathFiles;
      DBService.to.saveFileData(data);
    }
  } catch (e) {
    print(e);
  }
}

3. 存储数据库

由于项目比较简单,因此选用了比较轻量解决的非关系型数据库sembast,官方地址如下:
pub.dev/packages/se...

总的来说sembast非常简单,简单使用的话只需要创建数据库和分表,然后做相对应的增删改查

3.1. 创建数据库

数据库初始化:初始化数据库

ini 复制代码
 Future initDb() async {
  var dir = await localfileDir();
  path = dir.path;
  // myLog(dir);
  var dbPath = '${dir.path}/my_database.db';
  // 判断文件是否存在
  File dbFile = File(dbPath);
  bool exists = await dbFile.exists();
  if (!exists) {
    // 没有创建过DB,开始创建DB
    await dir.create(recursive: true);
  }

  DatabaseFactory dbFactory = databaseFactoryIo;
  // 初始化完成
  database = await dbFactory.openDatabase(dbPath);
}

数据库初始化: 创建下载分区

ini 复制代码
// 下载分区
final downloadStore = intMapStoreFactory.store("download");

3.2. 增加数据

scss 复制代码
// 存储下载数据
Future saveFileData(DownloadItemModel data) async {
  if (database == null) {
    await initDb();
  }
  downloadStore.record(data.id).put(database!, data.toJson());
}

3.3. 查找数据

这里的查找数据是使用的批量查找,单独查找改成record并且传参换成单独的id即可

csharp 复制代码
// 查找下载的数据
Future getFileData(List<int> ids) async {
  if (database == null) {
    await initDb();
  }
  var map = await downloadStore.records(ids).get(database!);
  List<DownloadItemModel> list = [];
  if (map.isEmpty) {
    return null;
  } else {
    for (var element in map) {
      if (element != null) {
        DownloadItemModel info = DownloadItemModel.fromJson(element);
        list.add(info);
      }
    }

    return list;
  }
}

全部的步骤到这里基本就完成了,剩下的就可以根据自己的业务需求完成扩展,最终代码已上传至下方仓库,有需要欢迎fork和讨论

代码地址

github.com/lixiaoblack...

相关推荐
耳東陈6 分钟前
Flutter开箱即用一站式解决方案-新增企业级日志
flutter
顾林海16 分钟前
Flutter 图片组件全面解析:从基础加载到高级应用
android·前端·flutter
眼镜会飞17 分钟前
Flutter window和Mac中webview2使用Cef替代
windows·flutter·mac
淡写成灰19 分钟前
Flutter自定义带有Badger组件组
flutter
好的佩奇1 小时前
Dart 之任务
android·flutter·dart
豪冷啊14 小时前
Flutter Invalid constant value.
flutter
顾林海18 小时前
Flutter容器组件深度解析
android·前端·flutter
xq952718 小时前
mac os flutter 配置环境变量
flutter
sg_knight21 小时前
Flutter性能优化终极指南:从JIT到AOT的深度调优
前端·flutter·性能优化·web·dart
zonda的地盘1 天前
[Get 源码] GetPageRoute 与 GetxController 的自动回收机制
flutter