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...

相关推荐
我要最优解3 小时前
关于在mac中配置Java系统环境变量
java·flutter·macos
江上清风山间明月2 天前
Flutter开发的应用页面非常多时如何高效管理路由
android·flutter·路由·页面管理·routes·ongenerateroute
Zsnoin能2 天前
flutter国际化、主题配置、视频播放器UI、扫码功能、水波纹问题
flutter
早起的年轻人2 天前
Flutter CupertinoNavigationBar iOS 风格导航栏的组件
flutter·ios
HappyAcmen2 天前
关于Flutter前端面试题及其答案解析
前端·flutter
coooliang2 天前
Flutter 中的单例模式
javascript·flutter·单例模式
coooliang2 天前
Flutter项目中设置安卓启动页
android·flutter
JIngles1232 天前
flutter将utf-8编码的字节序列转换为中英文字符串
java·javascript·flutter
B.-2 天前
在 Flutter 中实现文件读写
开发语言·学习·flutter·android studio·xcode
freflying11193 天前
使用jenkins构建Android+Flutter项目依赖自动升级带来兼容性问题及Jenkins构建速度慢问题解决
android·flutter·jenkins