监听各上传任务或者下载的百分比,并进行缓存本地进行断点续传

要将上传或下载的任务信息存储在本地,你可以使用数据库或文件存储。下面是使用文件存储的示例代码:

dart
import 'package:flutter/material.dart';
import 'package:dio/dio.dart';
import 'dart:io';
import 'dart:convert';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'File Transfer',
      home: TransferPage(),
    );
  }
}

class TransferPage extends StatefulWidget {
  @override
  _TransferPageState createState() => _TransferPageState();
}

class TransferTask {
  final String name;
  final String filePath;
  final String fileUrl;
  final String savePath;
  final int totalBytes;
  int transferredBytes;
  bool isPaused;

  TransferTask({
    required this.name,
    required this.filePath,
    required this.fileUrl,
    required this.savePath,
    required this.totalBytes,
    this.transferredBytes = 0,
    this.isPaused = false,
  });

  Map<String, dynamic> toMap() {
    return {
      'name': name,
      'filePath': filePath,
      'fileUrl': fileUrl,
      'savePath': savePath,
      'totalBytes': totalBytes,
      'transferredBytes': transferredBytes,
      'isPaused': isPaused,
    };
  }

  factory TransferTask.fromMap(Map<String, dynamic> map) {
    return TransferTask(
      name: map['name'],
      filePath: map['filePath'],
      fileUrl: map['fileUrl'],
      savePath: map['savePath'],
      totalBytes: map['totalBytes'],
      transferredBytes: map['transferredBytes'],
      isPaused: map['isPaused'],
    );
  }

  String toJson() => json.encode(toMap());

  factory TransferTask.fromJson(String source) =>
      TransferTask.fromMap(json.decode(source));
}

class _TransferPageState extends State<TransferPage> {
  Dio dio = Dio();
  List<TransferTask> tasks = [];

  @override
  void initState() {
    super.initState();
    dio.interceptors.add(LogInterceptor());

    // 从文件中加载任务信息
    loadTasks();
  }

  void saveTasks() {
    List<Map<String, dynamic>> taskList = tasks.map((task) => task.toMap()).toList();
    String jsonTasks = json.encode(taskList);

    File file = File('tasks.json');
    file.writeAsStringSync(jsonTasks);
  }

  void loadTasks() {
    File file = File('tasks.json');
    if (file.existsSync()) {
      String jsonTasks = file.readAsStringSync();
      List<Map<String, dynamic>> taskList = json.decode(jsonTasks).cast<Map<String, dynamic>>();
      tasks = taskList.map((taskMap) => TransferTask.fromMap(taskMap)).toList();
    }
  }

  @override
  void dispose() {
    saveTasks(); // 在页面销毁时保存任务信息
    super.dispose();
  }

  Future<void> uploadFile(TransferTask task) async {
    try {
      File file = File(task.filePath);
      int sentBytes = 0;

      if (await file.exists()) {
        sentBytes = file.lengthSync(); // 获取已上传的字节数

        FormData formData = FormData.fromMap({
          'file': await MultipartFile.fromFile(
            task.filePath,
            start: sentBytes, // 设置上传起始位置
          ),
        });

        await dio.post(
          'https://example.com/upload',
          data: formData,
          onSendProgress: (int sentBytes, int totalBytes) {
            setState(() {
              task.transferredBytes = sentBytes;
            });
          },
          cancelToken: CancelToken()
            ..pauseOnCancel = true
            ..token = CancelToken().token,
        );
      }
    } catch (e) {
      print('上传失败: $e');
    }
  }

  Future<void> downloadFile(TransferTask task) async {
    try {
      File file = File(task.savePath);
      int receivedBytes = 0;

      if (await file.exists()) {
        receivedBytes = file.lengthSync(); // 获取已下载的字节数
      }

      await dio.download(
        task.fileUrl,
        task.savePath,
        onReceiveProgress: (int receivedBytes, int totalBytes) {
          setState(() {
            task.transferredBytes = receivedBytes;
          });
        },
        options: Options(
          headers: {'range': 'bytes=$receivedBytes-'}, // 设置下载起始位置
        ),
        cancelToken: CancelToken()
          ..pauseOnCancel = true
          ..token = CancelToken().token,
      );
    } catch (e) {
      print('下载失败: $e');
    }
  }

  Future<void> uploadAll() async {
    for (TransferTask task in tasks) {
      task.isPaused = false;
      await uploadFile(task);
    }
  }

  Future<void> downloadAll() async {
    for (TransferTask task in tasks) {
      task.isPaused = false;
      await downloadFile(task);
    }
  }

  void pauseUpload(TransferTask task) {
    task.isPaused = true;
  }

  void continueUpload(TransferTask task) {
    if (task.isPaused) {
      task.isPaused = false;
      uploadFile(task);
    }
  }

  void pauseDownload(TransferTask task) {
    task.isPaused = true;
  }

  void continueDownload(TransferTask task) {
    if (task.isPaused) {
      task.isPaused = false;
      downloadFile(task);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('File Transfer'),
      ),
      body: ListView.builder(
        itemCount: tasks.length,
        itemBuilder: (context, index) {
          TransferTask task = tasks[index];
          return ListTile(
            title: Text(task.name),
            subtitle: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text('已传输:${task.transferredBytes} / ${task.totalBytes} 字节'),
                LinearProgressIndicator(
                  value: task.transferredBytes / task.totalBytes,
                ),
              ],
            ),
            trailing: Row(
              mainAxisSize: MainAxisSize.min,
              children: [
                if (task.transferredBytes < task.totalBytes && !task.isPaused)
                  IconButton(
                    onPressed: () => pauseDownload(task),
                    icon: Icon(Icons.pause),
                  ),
                if (task.transferredBytes < task.totalBytes && task.isPaused)
                  IconButton(
                    onPressed: () => continueDownload(task),
                    icon: Icon(Icons.play_arrow),
                  ),
                if (task.transferredBytes == 0 && !task.isPaused)
                  IconButton(
                    onPressed: () => pauseUpload(task),
                    icon: Icon(Icons.pause),
                  ),
                if (task.transferredBytes == 0 && task.isPaused)
                  IconButton(
                    onPressed: () => continueUpload(task),
                    icon: Icon(Icons.play_arrow),
                  ),
              ],
            ),
          );
        },
      ),
      floatingActionButton: Row(
        mainAxisAlignment: MainAxisAlignment.end,
        children: [
          FloatingActionButton(
            onPressed: uploadAll,
            child: Icon(Icons.cloud_upload),
          ),
          SizedBox(width: 16),
          FloatingActionButton(
            onPressed: downloadAll,
            child: Icon(Icons.cloud_download),
          ),
        ],
      ),
    );
  }
}
在上述代码中,我们将任务信息保存在名为 tasks.json 的文件中。使用 saveTasks 方法将任务信息转换为 JSON 格式并写入文件中,使用 loadTasks 方法从文件中加载任务信息并将其转换回对象数组。

在 _TransferPageState 类的 initState 方法中调用 loadTasks 来加载保存的任务信息,在 dispose 方法中调用 saveTasks 来保存任务信息。

这样,每次打开应用程序时,都会加载上次的任务状态,以便你可以继续之前的上传或下载操作。

封装单例

当我们需要在任意地方方便地进行上传或下载任务的管理时,可以使用单例模式来封装任务管理器,并在该任务管理器中提供方法来处理上传和下载任务。

首先,我们将创建一个名为 TransferManager 的单例类,用于管理上传和下载任务。该类应具有以下功能:

创建单例实例:

定义一个私有的构造函数,防止直接创建实例。
定义一个静态私有变量 _instance 来保存单例实例。
定义一个静态公有方法 getInstance(),用于获取单例实例。
任务管理:

在 TransferManager 类中定义一个私有变量 tasks 来保存所有任务信息。
提供用于添加、移除和获取任务的公有方法,例如 addTask()、removeTask() 和 getTasks()。
上传和下载任务:

在 TransferManager 类中提供公有方法 uploadFile() 和 downloadFile(),用于执行上传和下载任务。
下面是实现以上功能的示例代码:

dart
class TransferManager {
  static TransferManager? _instance;
  List<TransferTask> tasks = [];

  factory TransferManager.getInstance() {
    _instance ??= TransferManager._();
    return _instance!;
  }

  TransferManager._();

  void addTask(TransferTask task) {
    tasks.add(task);
  }

  void removeTask(TransferTask task) {
    tasks.remove(task);
  }

  List<TransferTask> getTasks() {
    return tasks;
  }

  Future<void> uploadFile(TransferTask task) async {
    // 上传文件的逻辑
    // ...
  }

  Future<void> downloadFile(TransferTask task) async {
    // 下载文件的逻辑
    // ...
  }
}
使用 TransferManager 类,你可以在任何地方方便地管理上传和下载任务。首先,通过 TransferManager.getInstance() 获取单例实例,然后就可以使用其提供的方法来添加、移除和获取任务,以及执行上传和下载操作。

例如,在你的应用程序中,可以这样使用:

dart
// 创建单例实例
TransferManager manager = TransferManager.getInstance();

// 添加任务
TransferTask task1 = TransferTask(...);
manager.addTask(task1);

// 获取任务列表
List<TransferTask> tasks = manager.getTasks();

// 执行上传任务
manager.uploadFile(task1);

// 移除任务
manager.removeTask(task1);

// 执行下载任务
manager.downloadFile(task1);
通过封装任务管理器和相关方法,你可以更便捷地管理上传和下载任务,并且可以在任何地方使用同一个单例实例进行任务管理。这样,你就能够更灵活地控制和操作任务,满足不同场景下的需求。

上传model例子

当你调用 downloadFile 方法时,只需要传递一个具有合适属性值的 TransferTask 对象,就可以在方法中使用该对象来执行下载操作。

例如:

dart
TransferTask task = TransferTask(
  url: 'https://example.com/file.txt',
  destinationPath: '/path/to/save/file.txt',
  isPaused: false,
  downloadedBytes: 0,
  totalBytes: 0,
);

await downloadFile(task);
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 161,780评论 4 369
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,424评论 1 305
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 111,397评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,576评论 0 218
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,997评论 3 295
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,945评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,107评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,850评论 0 208
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,625评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,804评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,285评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,613评论 3 261
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,291评论 3 242
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,164评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,963评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,096评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,886评论 2 278

推荐阅读更多精彩内容