如何将文件保存到 Android 上的下载文件夹 @% 33
原标题:How to Save file to Downloads folder on Android >=33
I ve a flutter app with audio files downloaded from network. I want to download an audio file to my devices Downloads folder to make it usable for later use like Making it a Ringtone or similar.
Previously I used WRITE_EXTERNAL_STORAGE permission via permission handler package s Permission.storage.
From their doc,
As of Android SDK 30 (Android 11) the WRITE_EXTERNAL_STORAGE permission is considered a high-risk or sensitive permission. There for it is required to declare the use of these permissions if you intend to release the application via the Google Play Store.
So, to save file on Downloads folder, I need MANAGE_EXTERNAL_STORAGE (via Permissions. manageExternalStorage). But when I add this permission and submit my app, the update is rejected via google saying that this is not a core functionality of my add and I should use less risky APIs to access data.
So, if I target API 33 or above, how can I download an audio file to my Android devices Downloads folder?
问题回答
I faced the same situation and found a solution that might be helpful. Instead of relying on the MANAGE_EXTERNAL_STORAGE permission, I chose to directly assign the path /storage/emulated/0/Download to save the files.
import dart:io ;
import package:flutter/foundation.dart ;
import package:path_provider/path_provider.dart ;
static Future saveFile(String fileName, List bytes) async {
Directory? directory;
File? file;
try {
if (defaultTargetPlatform == TargetPlatform.android) {
//downloads folder - android only - API>30
directory = Directory( /storage/emulated/0/Download );
} else {
directory = await getApplicationDocumentsDirectory();
}
bool hasExisted = await directory.exists();
if (!hasExisted) {
directory.create();
}
//file to saved
file = File("${directory.path}${Platform.pathSeparator}$fileName.pdf");
if (!file.existsSync()) {
await file.create();
}
await file.writeAsBytes(bytes);
} catch (e) {
if (file != null && file.existsSync()) {
file.deleteSync();
}
rethrow;
}
}
This solution worked for me; I hope it proves useful in solving your issue as well.
Apparently, on Android>=33, to write file on external storage, you will not need any permissions at all!
More recent versions of Android rely more on a file s purpose than its location for determining an app s ability to access, and write to, a given file. In particular, if your app targets Android 11 (API level 30) or higher, the WRITE_EXTERNAL_STORAGE permission doesn t have any effect on your app s access to storage. This purpose-based storage model improves user privacy because apps are given access only to the areas of the device s file system that they actually use.
https://developer.android.com/training/data-storage
Android: The following code works for Android 11 and above (API level 30+)
As per Google Play store new policy there is no need to get/request any permission to write or read files from storage. This is applicable only for (your) App owned directory such as: Cache, Temporary, External, Document, Download directories etc.
But to get access of this directory /storage/emulated/0/Download or at this level, you must get permission using permission_handler like as Permission.manageExternalStorage.request() and must declare permission in Android Manifest file .
If your app using Permission.manageExternalStorage.request() such then while uploading to PlayStore you must submit details and reason to use such high level permission. You can read more about this MANAGE_EXTERNAL_STORAGE permission here.
import dart:io ;
import package:dio/dio.dart ;
import package:flutter/material.dart ;
import package:path_provider/path_provider.dart ;
class HomeScreen extends StatefulWidget {
const HomeScreen({
super.key,
});
@override
State createState() => _HomeScreenState();
}
class _HomeScreenState extends State with TickerProviderStateMixin {
final String imageUrl =
https://satsang-foundation.org/wp-content/uploads/2022/08/Ganesha-Chathurthi-TSF-4.jpg ;
final Dio dio = Dio();
double progress = 0.0;
bool loading = false;
Future saveFile(String url) async {
try {
if (Platform.isAndroid) {
Directory? directory;
directory = await getDownloadsDirectory();
String path = ${directory!.path}/Shri Ganesha.jpeg ;
File savePath = File(path);
await dio.download(
url,
savePath.path,
onReceiveProgress: (downloadedSize, totalSize) {
setState(() {
progress = downloadedSize / totalSize;
});
},
);
}
} catch (e) {
print(e);
}
return false;
}
void downloadImage() async {
bool download = await saveFile(imageUrl);
}
@override
void initState() {
// TODO: implement initState
super.initState();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
backgroundColor: Theme.of(context).colorScheme.onPrimary,
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Image.network(
imageUrl,
fit: BoxFit.fitHeight,
),
),
LinearProgressIndicator(
minHeight: 10.0,
value: progress,
),
ElevatedButton(
onPressed: downloadImage,
child: const Text( Download ),
),
],
),
),
),
);
}
}
I know it s late to reply but maybe useful for someone else.
I think you better manage directory as per Platforms. For example in iOS use getApplicationDocumentsDirectory() and for android use getDownloadsDirectory(). Here in the case of android files will be saved to /storage/emulated/0/android/com.xxx.xxx/files/downloads/file.extension.
sample code is,
final appSupportDir = Platform.isAndroid
? await getDownloadsDirectory()
: await getApplicationDocumentsDirectory();
NB: Note that, files saved to this folder maybe deleted when the application is uninstalled.
相关问题
Android - ListView fling gesture triggers context menu
I m relatively new to Android development. I m developing an app with a ListView. I ve followed the info in #1338475 and have my app recognizing the fling gesture, but after the gesture is complete, ...
AsyncTask and error handling on Android
I m converting my code from using Handler to AsyncTask. The latter is great at what it does - asynchronous updates and handling of results in the main UI thread. What s unclear to me is how to handle ...
Android intent filter for a particular file extension?
I want to be able to download a file with a particular extension from the net, and have it passed to my application to deal with it, but I haven t been able to figure out the intent filter. The ...
Android & Web: What is the equivalent style for the web?
I am quite impressed by the workflow I follow when developing Android applications: Define a layout in an xml file and then write all the code in a code-behind style. Is there an equivalent style for ...
TiledLayer equivalent in Android [duplicate]
To draw landscapes, backgrounds with patterns etc, we used TiledLayer in J2ME. Is there an android counterpart for that. Does android provide an option to set such tiled patterns in the layout XML?
Using Repo with Msysgit
When following the Android Open Source Project instructions on installing repo for use with Git, after running the repo init command, I run into this error:
/c/Users/Andrew Rabon/bin/repo: line
...
Android "single top" launch mode and onNewIntent method
I read in the Android documentation that by setting my Activity s launchMode property to singleTop OR by adding the FLAG_ACTIVITY_SINGLE_TOP flag to my Intent, that calling startActivity(intent) would ...
From Web Development to Android Development
I have pretty good skills in PHP , Mysql and Javascript for a junior developer. If I wanted to try my hand as Android Development do you think I might find it tough ?
Also what new languages would I ...