【问题标题】:Flutter - download files of any type on Android to external storage i.e. Downloads directoryFlutter - 将 Android 上任何类型的文件下载到外部存储,即下载目录
【发布时间】:2021-10-30 17:56:20
【问题描述】:

几乎所有移动应用程序框架似乎都能够将文件下载到“Downloads”目录,但 Flutter 除外。还是只有我一个人?

自 Android SDK 29 以来,Flutter (Android) 似乎已经采取了只允许将文件下载到作用域应用程序目录或应用程序临时缓存目录的方向。我可能完全错了,这就是我在 SO 上发帖的原因,希望我是。

您可以在 Android 上下载,但问题是您必须编写教程让用户尝试找到您下载的任何内容,因为您可以在 Flutter 中下载文件。除了应用程序应用程序目录之外无法访问或无法找到应用程序临时缓存目录。

  1. 第一个问题,为什么没有简单的方法来获得权限并将Android上的文件下载到Downloads或media目录?或者更好的是一个对话框,用户可以在其中选择他们想要下载文件的位置。我获得了安全性,但 Slack 应用以某种方式做到了。

在 Slack 的应用程序上,我可以下载一个文件并将其保存到下载。没什么大不了的。

关于过时的颤振包有很多关于 SO 的建议:

  • ext_storage 已过时,即使您可以获得下载目录路径,也无法仅使用 Flutter/Dart 对其进行写入。
  • image_gallery_saver 似乎找到了解决此限制的方法,但它仅适用于某些文件类型。例如。由于某种原因,它不适用于音频文件。
  • downloads_path_provider 与第 1 条相同的问题,无论如何都不再维护。
  • path_provider 有一个 getExternalStorageDirectory() 函数。您会假设它会返回 Android 上的外部存储选项,但它当前返回的只是应用程序文档目录(应用程序外的用户无法访问)和临时应用程序缓存目录,用户在没有教程的情况下无法找到.
  • Just reference the path to emulated storage /storage/emulated/0/Download 在 2021 年 10 月 30 日失效。即使您已获得 storage 权限,也没有写入权限。

Flutter 需要能够将 Android 上的文件下载到 Downloads 或 media 目录。有很多正当理由可以让您从 Flutter 应用下载文件,该文件需要在公共的外部存储中访问,并且可以通过 Android 的 Files 应用轻松找到。

无论如何,Flutter 开发人员在 2021 年 10 月在这里有哪些选项可以将 Android 上的文件保存到下载目录?顺便说一句,这只是 iOS 上的默认设置。

目前,在 Android 上,Flutter 开发人员的最佳选择是使用 url_launcher 之类的东西并制作一个强制下载的 url,然后访问浏览器的下载管理器。现在是 Android 的最佳方案吗?

最后,我喜欢 Flutter,但这个特定领域似乎需要一些爱,因此 Flutter 开发人员不会落后于其他框架。

【问题讨论】:

    标签: flutter


    【解决方案1】:

    事实证明,对于 2021 年 10 月的 Flutter,Android 有一个很好的解决方案。我需要的缺少的包是 flutter_file_dialog 包,它使用 Kotlin 添加所需的编程以将文件保存在 Flutter 应用用户想要在其 Android 设备上的任何位置,包括下载。

    我使用的包:

    对于 android,流程是:

    1. 确保您首先拥有存储权限。
    2. 我正在使用 GCS 和 Firebase,因此我为私人内容创建了短暂的安全 URL。不包括代码。
    3. 使用 path_provider getTemporaryDirectory() 获取应用程序的作用域缓存目录路径。
    4. 使用 Dio 将短暂的 url 保存到临时缓存中
    5. 最后,使用 flutter_file_dialog 打开保存到对话框并允许用户将下载的文件保存到其中。

    编码 sn-ps 以查看流程:

    try {
      /// Verify we have storage permissions first.
      /// Code snippet showing me calling my devicePermissions service provider
      /// to request "storage" permission on Android.
      await devicePermissions
          .storagePermissions()
          .then((granted) async {
        /// Get short lived url from google cloud storage for non-public file
        final String? shortLivedUrl...
    
        if (shortLivedUrl == null) {
          throw Exception('Could not generate a '
              'downloadable url. Please try again.');
        }
    
        final String url = shortLivedUrl;
        print(url);
    
        /// Get just the filename from the short lived (super long) url
        final String filename =
            Uri.parse(url).path.split("/").last;
        print('filename: $filename');
    
        Directory? directory;
    
        if (Platform.isIOS) {
          directory = await getDownloadsDirectory();
          print(directory?.path);
        } else if (Platform.isAndroid) {
          /// For Android get the application's scoped cache directory
          /// path.
          directory = await getTemporaryDirectory();
        }
    
        if (directory == null) {
          throw Exception('Could not access local storage for '
              'download. Please try again.');
        }
    
        print(
            'Temp cache save path: ${directory.path}/$filename');
    
        /// Use Dio package to download the short lived url to application cache
        await dio.download(
          url,
          '${directory.path}/$filename',
          onReceiveProgress: _showDownloadProgress,
        );
    
        /// For Android call the flutter_file_dialog package, which will give
        /// the option to save the now downloaded file by Dio (to temp
        /// application cache) to wherever the user wants including Downloads!
        if (Platform.isAndroid) {
          final params = SaveFileDialogParams(
              sourceFilePath: '${directory.path}/$filename');
          final filePath =
              await FlutterFileDialog.saveFile(params: params);
    
          print('Download path: $filePath');
        }
      });
    } on DevicePermissionException catch (e) {
      print(e);
      await OverlayMessage.showAlertDialog(
        context,
        title: 'Need Storage Permission',
        message: devicePermissions.errorMessage,
        barrierDismissible: true,
        leftAction: (context, controller, setState) {
          return TextButton(
            child: Text('Cancel'),
            onPressed: () async {
              controller.dismiss();
            },
          );
        },
        rightAction: (context, controller, setState) {
          return TextButton(
            child: Text('Open App Settings'),
            onPressed: () async {
              controller.dismiss();
              Future.delayed(Duration.zero, () async {
                /// Using permission_handler package we can easily give
                /// the user the option to tap and open settings from the
                /// app and manually allow storage.
                await devicePermissions.openManualAppSettings();
              });
            },
          );
        },
      );
    } catch (e) {
      print(e.toString());
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-03-24
      • 1970-01-01
      • 1970-01-01
      • 2017-12-05
      相关资源
      最近更新 更多