Flutter文件保存插件utopia_save_file的使用

发布于 1周前 作者 phonegap100 来自 Flutter

Flutter文件保存插件utopia_save_file的使用

简介

utopia_save_file 是一个统一且正确的文件保存实现,适用于Flutter应用。它提供了多种方法来保存文件,包括从URL、字节流和文件对象中保存。该插件在不同平台(如Android、iOS和Web)上有不同的行为,但都尽量保持一致的API。

一般行为

  1. MIME类型和文件名推断:如果未提供MIME类型和文件名,插件会尝试推断。如果无法推断,则会抛出 SaveFileMetadataException 异常。
  2. 文件扩展名检查:检查文件名是否包含与MIME类型匹配的扩展名。如果不匹配,则根据 extensionBehavior 参数进行处理,默认情况下会替换原始扩展名。
  3. 触发保存操作:启动保存操作并等待完成,同时尽量减少Dart线程的工作量。

平台特定注意事项

Android
  • 在Android上,插件会启动系统“文件”应用程序,允许用户选择保存的目标位置和文件名。
  • 用户可以取消操作,此时 fromX 方法将返回 SaveFileResultCancelled
  • 使用 Intent.ACTION_CREATE_DOCUMENTContentResolver.openInputStream 实现文件保存。
  • 不需要 WRITE_EXTERNAL_STORAGE 权限。
  • name 参数只是一个建议,用户可以在保存时更改文件名。
iOS
  • 在iOS上,文件保存到应用程序的文档目录,但文件会在系统“Files”应用程序中可见。
  • 不需要用户交互,因此 fromX 方法总是返回 true
  • 需要在 Info.plist 中添加以下配置:
    <key>NSAllowsArbitraryLoads</key>
    <true/>
    <key>LSSupportsOpeningDocumentsInPlace</key>
    <true/>
    <key>UIFileSharingEnabled</key>
    <true/>
    
Web
  • 在Web上,插件使用带有 download 属性的 <a> 元素触发下载,下载完全在应用外部进行。
  • fromX 方法会立即返回 true,即使用户后来取消了下载。

完整示例代码

下面是一个完整的示例代码,展示了如何使用 utopia_save_file 插件来保存文件。这个示例包括从URL、字节流和文件对象中保存文件的功能。

import 'dart:async';
import 'dart:typed_data';

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

// 根据平台导入不同的下载模块
import 'download_io.dart' if (dart.library.js) 'download_web.dart';

const _fileUrl = "https://upload.wikimedia.org/wikipedia/commons/8/80/Wikipedia-logo-v2.svg?download";
const _fileUrlName = "wiki_logo";
const _fileBytesName = "alamakota";
const _fileBytesMime = "text/plain";

// 构建字节流
Stream<List<int>> _buildFileBytes() =>
    Stream.fromIterable(["ala", "ma", "kota"]).map((it) => Uint8ClampedList.fromList(it.codeUnits));

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

class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);

  [@override](/user/override)
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  bool _isInProgress = false;

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('UtopiaSaveFile example')),
        body: Builder(
          builder: (context) => Center(
            child: _isInProgress ? _buildLoader() : _buildButtons(context),
          ),
        ),
      ),
    );
  }

  // 构建加载指示器
  Widget _buildLoader() => const CircularProgressIndicator();

  // 构建按钮
  Widget _buildButtons(BuildContext context) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        ElevatedButton(
          onPressed: () => _saveUrl(context, useName: false),
          child: const Text("Save without name"),
        ),
        const SizedBox(height: 8),
        ElevatedButton(
          onPressed: () => _saveUrl(context, useName: true),
          child: const Text("Save with name"),
        ),
        const SizedBox(height: 8),
        ElevatedButton(
          onPressed: () => _saveBytes(context),
          child: const Text("Save bytes"),
        ),
        const SizedBox(height: 8),
        ElevatedButton(
          onPressed: () => _saveFile(context),
          child: const Text("Save file"),
        ),
      ],
    );
  }

  // 从URL保存文件
  Future<void> _saveUrl(BuildContext context, {required bool useName}) async =>
      _runWithProgress(context, () => UtopiaSaveFile.fromUrl(_fileUrl, name: useName ? _fileUrlName : null));

  // 从字节流保存文件
  Future<void> _saveBytes(BuildContext context) async {
    await _runWithProgress(
      context,
      () => UtopiaSaveFile.fromByteStream(_buildFileBytes(), name: _fileBytesName, mime: _fileBytesMime),
    );
  }

  // 从文件对象保存文件
  Future<void> _saveFile(BuildContext context) async =>
      _runWithProgress(context, () async => UtopiaSaveFile.fromFile(await download(_fileUrl), name: _fileUrlName));

  // 带进度显示的操作
  Future<void> _runWithProgress(BuildContext context, Future<bool> Function() block) async {
    late String message;
    try {
      setState(() => _isInProgress = true);
      message = await block() ? "Success" : "Cancelled";
    } catch (_) {
      message = "Failed";
      rethrow;
    } finally {
      setState(() => _isInProgress = false);
      ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message)));
    }
  }
}

更多关于Flutter文件保存插件utopia_save_file的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter文件保存插件utopia_save_file的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter项目中使用utopia_save_file插件来保存文件的代码示例。请注意,实际使用时,你可能需要根据自己的项目结构和需求进行调整。

首先,确保你已经在pubspec.yaml文件中添加了utopia_save_file依赖:

dependencies:
  flutter:
    sdk: flutter
  utopia_save_file: ^最新版本号  # 请替换为实际的最新版本号

然后,运行flutter pub get来安装依赖。

接下来,在你的Dart文件中导入utopia_save_file插件,并使用它来保存文件。以下是一个简单的示例:

import 'package:flutter/material.dart';
import 'package:utopia_save_file/utopia_save_file.dart';
import 'dart:io';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: SaveFileDemo(),
    );
  }
}

class SaveFileDemo extends StatefulWidget {
  @override
  _SaveFileDemoState createState() => _SaveFileDemoState();
}

class _SaveFileDemoState extends State<SaveFileDemo> {
  String _result = '';

  void _saveFile() async {
    // 要保存的文件内容
    String content = "Hello, this is a test file content.";

    // 获取外部存储目录(这里以Android为例,iOS类似但路径不同)
    Directory externalDir = await getExternalStorageDirectory();
    if (externalDir == null) {
      setState(() {
        _result = "Failed to get external storage directory.";
      });
      return;
    }

    // 构建文件路径和文件名
    File file = File('${externalDir.path}/test_file.txt');

    // 写入文件
    try {
      await file.writeAsString(content);
      setState(() {
        _result = "File saved successfully to ${file.path}.";
      });
    } catch (e) {
      setState(() {
        _result = "Failed to save file: $e.";
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Save File Demo'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              _result,
              style: TextStyle(fontSize: 18),
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: _saveFile,
              child: Text('Save File'),
            ),
          ],
        ),
      ),
    );
  }
}

注意事项

  1. 权限处理:在Android上,你需要在AndroidManifest.xml文件中请求存储权限。同时,在运行时检查并请求权限(Flutter有相关的权限请求插件,如permission_handler)。

  2. iOS路径:iOS上文件保存的路径和Android不同,通常你会保存到应用的沙盒目录中,如Documents目录。

  3. 错误处理:在实际应用中,你应该更细致地处理各种可能的错误情况,比如存储空间不足、权限被拒绝等。

  4. 插件版本:确保你使用的是最新版本的utopia_save_file插件,因为插件的API可能会随着版本更新而变化。

这个示例展示了如何使用utopia_save_file(或类似的文件保存逻辑)来在Flutter应用中保存文本文件。根据你的需求,你可以扩展这个示例来保存其他类型的文件,比如图片、JSON数据等。

回到顶部