几乎所有的移动应用框架似乎都有能力将文件下载到"Downloads"目录,除了Flutter还是只有我这么想?
从Android SDK 29开始,Flutter (Android)似乎只允许将文件下载到限定范围的应用程序目录或应用程序的临时缓存目录。我可能完全错了,这就是为什么我在SO上发帖,希望我是。
你可以在Android上下载,但问题是你必须为用户写一个教程,让他们尝试找到你下载的任何东西,因为你可以在Flutter上下载文件。
应用程序临时缓存目录- 第一个问题,为什么没有一种简单的方法来获得权限并下载文件到Android上的下载或媒体目录?或者最好有一个对话框,用户可以选择他们想要下载的文件。我得到了安全,但Slack应用程序不知怎么做到了。
在Slack的应用程序上,我可以下载一个文件,它将保存到下载。没什么大不了的。
在SO上有很多关于过时的flutter包的建议:
- ext_storage是过时的,即使你可以得到下载目录路径,你也不能只使用Flutter/Dart写入它。
- image_gallery_saver似乎找到了解决此限制的方法,但它仅适用于某些文件类型。例如,由于某些原因,它不能与音频文件一起工作。
- downloads_path_provider与子弹头1相同的问题,无论如何都不再维护。
- path_provider有一个
getExternalStorageDirectory()
函数。你可能会认为它会在Android上返回外部存储选项,但它目前返回的是应用程序文档目录(应用程序外的用户无法访问)和临时应用程序缓存目录,如果没有教程,用户是不可能找到的。 - 仅引用模拟存储
/storage/emulated/0/Download
的路径不工作,2021年10月30日。 没有写权限,即使你有
storage
权限。Flutter需要能够将Android上的文件下载到Downloads或media目录。有很多合理的理由让你从一个Flutter应用中下载一个文件,而这个文件需要在外部公共存储中访问,并且可以很容易地通过Android的Files应用找到。
无论如何,在2021年10月,Flutter开发人员有什么选择将Android上的文件保存到下载目录??顺便说一句,这只是iOS的默认设置。
现在,对于Android上的Flutter开发者来说,最好的选择是使用url_launcher之类的东西,并制作一个强制下载的url,这将反过来访问浏览器的下载管理器。Android目前的最佳情况是什么?
最后,我喜欢Flutter,但是这个特殊的领域似乎需要一些爱,这样Flutter开发人员就不会落后于其他框架。
对于2021年10月的Flutter,事实证明这是Android的一个很好的解决方案。我需要的缺少的包是flutter_file_dialog包,它使用Kotlin添加所需的编程,以便将文件保存在Flutter应用程序用户想要在其Android设备上的任何地方,包括下载。
我使用的包:
- path_provider
- flutter_file_dialog
- 戴奥
- permission_handler
- flash(当设备存储权限被阻塞时警告库)
对于android,流程为:
- 请确保您有存储权限。
- 我正在使用GCS和Firebase,所以我创建了短暂的安全url到私人内容。
- 使用path_provider
getTemporaryDirectory()
获取应用程序的作用域缓存目录路径。 - 使用Dio保存短时间url到临时缓存
- 最后,使用flutter_file_dialog打开一个保存对话框,并允许用户将下载的文件保存到哪里。
查看流程的代码片段:
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());
}