Flutter/Dart - File.writeAsBytes executes with no error but can't find the file - flutter

I have a base64 encoded string of a PDF file. I'm trying to save it as a file in the android device. My code runs with no error, but i can't find the file.
Uint8List bytes = base64.decode(encodedStr);
String dir = (await getExternalStorageDirectories(
type: StorageDirectory.downloads))!
.first
.path;
File file = File("$dir/" +
DateTime.now().millisecondsSinceEpoch.toString() +
".pdf");
await file.writeAsBytes(bytes);
Also, I tried to include code to ask for permission before trying to save the file, but it didn't change anything.
var status = await Permission.storage.status;
if (!status.isGranted) {
await Permission.storage.request();
}
if (await Permission.storage.request().isGranted) {
try {
await file.writeAsBytes(bytes);
} catch (e) {
print(e);
}
}
And, as another attempt, I included these permission on my AndroidManifest file. Also, no changes.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION"/>
<uses-permission android:name="android.permission.READ_INTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
//dir = /storage/emulated/0/Android/data/com.xxxxxxxxxxxxxxxx.xxxxx/files/Download
//dir + file = /storage/emulated/0/Android/data/com.xxxxxxxxxxxxxxxx.xxxxx/files/Download/1655924904315.pdf
//file path after written = /storage/emulated/0/Android/data/com.xxxxxxxxxxxxxxxx.xxxxx/files/Download/1655924904315.pdf
Is there anything wrong with my code or something that I can change in order for it to work ?
The above code is using path_provider and permission_handler packages and was tested in an emulator and in a physical device.
Thanks.

Try importing io and use file of io and use this package https://pub.dev/packages/pdf
import 'dart:io' as io;
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
final pdf = pw.Document();
final dir = await getTemporaryDirectory();
final path = "${dir.path}/pdfname.pdf";
final file = await io.File(path).writeAsBytes(pdf.save());

It happens that the files are being correctly generated, but they are being saved in the application's directory. A download folder has being created and the files are being saved there. I didn't find the files searching via android, but when I connected my physical device to a PC I was able the find them.
It seems that this has something to do with this https://github.com/flutter/flutter/issues/35783 . Path_provider started to return application document directory after 1.1.0 instead of the general downloads folder.
I'll check how to access the path I want correctly.

Related

How to create a Button that allow user to download a specific file Flutter

I create a flutter app and I have this one CSV file that used as a template for user. I want to provide a Button that allow user to download this CSV file, so they can use it to have CSV file that already have our template.
The problem is I don't know if the best way is to first store the file online and get the url and use it on the flutter downloader URL or keep it in the local code asset and refer to that file when user tap the download template button. Currently I'm applying the second option and it doesn't work (I don't know if this option is possible or not), the download always fail. I'm using flutter_downloader package.
How to fix this ?
Here's my code, Is something wrong with my code ?
/// Check if the file exist or not
if (await File(externalDir!.path + "/" + fileName).exists()) {
OpenFilex.open(externalDir!.path + "/" + fileName);
} else {
/// Download the file if it doesn't exist in the user's device
final String localPath = (await getApplicationDocumentsDirectory()).path;
/// Dummy file name I want use (it exist in my asset dir"
const String fileName = 'add.png';
final data = await rootBundle.load('assets/logo/add.png');
final bytes = data.buffer.asUint8List();
final File file = File('$localPath/$fileName');
await file.writeAsBytes(bytes);
/// Download the file
final taskId = await FlutterDownloader.enqueue(
url: '',
savedDir: localPath,
fileName: fileName,
showNotification: true,
openFileFromNotification: true,
);
}
To load a file from the AppBundle and then save it to the users phone, do the following:
Put the file in assets/filename.csv and declare it in your pubspec like this:
flutter:
assets:
- assets/filename.csv
Load the file in your code:
import 'package:flutter/services.dart' show ByteData, rootBundle;
(...)
var data = (await rootBundle.load('assets/filename.csv)).buffer.asInt8List();
Save the data to a file (you need the path-provider package if you want to copy the exact code):
import 'package:path_provider/path_provider.dart' as pp;
(...)
var path = (await pp.getApplicationDocumentsDirectory()).path;
var file = File('$path/filename.csv');
await file.writeAsBytes(data, flush: true);
Edit: As Stephan correctly pointed out, if you want to store the file in the downloads folder, you will find additional information about that here. Thank you Stephan!

I need to save picture in a specific location flutter

I need to save picture in a specific location rather than in temporary location.
void _takePicture(BuildContext context) async {
try {
await _initializeCameraControllerFuture;
final path =
join((await getTemporaryDirectory()).path, '${DateTime.now()}.png');
await _cameraController.takePicture(path);
Navigator.pop(context,path);
} catch (e) {
print(e);
}
}
You need to save the image into external storage directory for showing the image on gallery. Instead of getting temporary directory, obtain external storage directory.
final directory = await getExternalStorageDirectory();
You need to provide the permission on AndroidManifest.xml file of your android/app/src/main folder
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
then let's say that you want to create a folder named MyImages and add the new image to that folder,
final myImagePath = '${directory.path}/MyImages' ;
final myImgDir = await new Directory(myImagePath).create();
then write to the file to the path.
var kompresimg = new File("$myImagePath/image_$baru$rand.jpg")
..writeAsBytesSync(img.encodeJpg(gambarKecilx, quality: 95));
for getting the number of files, just obtain the files to a list and check the length of the list
var listOfFiles = await myImgDir.list(recursive: true).toList();
var count = countList.length;
Or check this.

Permission Denied even after granting the Storage Permission

I have added every thing required to download files to local storage still I'm getting this error
[ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception:
FileSystemException: Creation failed, path =
'/storage/emulated/0/uth_content' (OS Error: Operation not permitted,
errno = 1)
In Manifest
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION"/>
android:requestLegacyExternalStorage="true"
android:hardwareAccelerated="true"
<application
android:requestLegacyExternalStorage="true" >
In pubspec.yaml
permission_handler: ^6.1.1
My Dart File
#override
Future<int> downloadZipFile() async{
Directory extStorage = await _externalDataSource.GetExtStoragePath();
if(extStorage !=null) {
var response = await _apiDataSource.downloadZipContent(extStorage.path);
File zipfile = await response.zipFile;
debugPrint("Downloaded file :" + zipfile.path + "Exist: " + zipfile.existsSync().toString());
return response.statusCode;
}
else{
debugPrint("Path is empty");
createFolder("uth_content");
}
}
Future<String> createFolder(String cow) async {
final folderName = cow;
final path = Directory("/storage/emulated/0/$folderName");
var status = await Permission.storage.status;
if (!status.isGranted) {
await Permission.storage.request();
}
if ((await path.exists())) {
return path.path;
} else {
path.create();
return path.path;
}
}
How can I ensure that the folder is created, my android api is 30
You are missing this permission in your AndroidManifest.xml
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
To get the permission use
if (await Permission.manageExternalStorage.request().isGranted) {...}
NOTE: This permission is considered pretty risky so play store will reject your app unless it's your app's core functionality.
This is what google says
Core functionality is defined as the main purpose of the app. Without
this core functionality, the app is "broken" or rendered unusable. The
core functionality, as well as any core features that comprise this
core functionality, must all be prominently documented and promoted in
the app's description.
And if you are planning to create a file in an external directory and you are not able to find it, it's because you'd have to tell the device to refresh for the files. So I'd recommend using this package and passing your newly created file path to it or if you wanna do it manually with kotlin here's the code
private fun broadcastFileUpdate(path: String) {
context.sendBroadcast(Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(File(path))))
println("updated!")
}

How to save File in Downloads folder in flutter?

In my flutter application I can create a pdf file, then I want to save it in Download folder.
I'm trying to achieve this goal with path_provider package, but I can't.
This is the sample code from flutter's cookbook, If I use it I don't get any error, but I don't find the file either.
final directory = await getApplicationDocumentsDirectory();
File file2 = File("${directory.path}/test.txt");
await file2.writeAsString('TEST ONE');
What's the correct way to do it?
To find the correct path please use ext_storage.
You will need this permission
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
on Android 10 you need this in your manifest
<application
android:requestLegacyExternalStorage="true"
on Android 11 use this instead
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
Remember to ask for them using permission_handler
I leave you my code:
static Future saveInStorage(
String fileName, File file, String extension) async {
await _checkPermission();
String _localPath = (await ExtStorage.getExternalStoragePublicDirectory(
ExtStorage.DIRECTORY_DOWNLOADS))!;
String filePath =
_localPath + "/" + fileName.trim() + "_" + Uuid().v4() + extension;
File fileDef = File(filePath);
await fileDef.create(recursive: true);
Uint8List bytes = await file.readAsBytes();
await fileDef.writeAsBytes(bytes);
}
Android 11 changed a lot of things with its emphasis on scoped storage. Although /storage/emulated/0/Android/data/com.my.app/files is one of the directory paths given by the path_provider pkg, you won't be able to see files saved in /storage/emulated/0/Android/data/com.my.app/files just using any run-of-the-mill file application (Google Files, Samsung My Files, etc.).
A way to get around this (although it only works on Android) is to specify the "general" downloads folder as shown below.
Directory generalDownloadDir = Directory('/storage/emulated/0/Download');
if you write whatever file you are trying to save to that directory, it will show up in the Downloads folder in any standard file manager application, rather than just the application-specific directory that the path_provider pkg provides.
Below is some test code from an app I'm building where I save a user-generated QR code to the user's device. Just for more clarity.
//this code "wraps" the qr widget into an image format
RenderRepaintBoundary boundary = key.currentContext!
.findRenderObject() as RenderRepaintBoundary;
//captures qr image
var image = await boundary.toImage();
String qrName = qrTextController.text;
ByteData? byteData =
await image.toByteData(format: ImageByteFormat.png);
Uint8List pngBytes = byteData!.buffer.asUint8List();
//general downloads folder (accessible by files app) ANDROID ONLY
Directory generalDownloadDir = Directory('/storage/emulated/0/Download'); //! THIS WORKS for android only !!!!!!
//qr image file saved to general downloads folder
File qrJpg = await File('${generalDownloadDir.path}/$qrName.jpg').create();
await qrJpg.writeAsBytes(pngBytes);
Fluttertoast.showToast(msg: ' $qrName QR code was downloaded to ' + generalDownloadDir.path.toString(), gravity: ToastGravity.TOP);
You want getExternalStorageDirectories. You can pass a parameter to specify the downloads specifically:
final directory = (await getExternalStorageDirectories(type: StorageDirectory.downloads)).first!;
File file2 = File("${directory.path}/test.txt");
await file2.writeAsString('TEST ONE');
If you're using null safety you don't need the bang operator:
final directory = (await getExternalStorageDirectories(type: StorageDirectory.downloads)).first;
File file2 = File("${directory.path}/test.txt");
await file2.writeAsString('TEST ONE');
For downloading file in Downloads folder, here are examples:
// Save multiple files
DocumentFileSave.saveMultipleFiles([textBytes, textBytes2], ["text1.txt", "text2.txt"], ["text/plain", "text/plain"]);
//Save single text file
DocumentFileSave.saveFile(textBytes, "my_sample_file.txt", "text/plain");
//Save single pdf file
DocumentFileSave.saveFile(pdfBytes, "my_sample_file.pdf", "appliation/pdf");
//Save single image file
DocumentFileSave.saveFile(imageJPGBytes, "my_sample_file.jpg", "image/jpeg");
More details of library here:
https://pub.dev/packages/document_file_save_plus

Locate path of assets files in flutter app

I am using a package (Starflut) that needs to use a file which I store in assets.
I first add this file to the pubspec.yaml:
assets:
- my_folder/my_file.py
I have then been trying to find this file while running the app, but it is in none of the following directories or any of its subfolders:
/data/user/0/com.example.my_app
/data/data/com.example.my_app
import 'package:path_provider/path_provider.dart';
import 'dart:io';
...
var aux=Directory("/data/user/0/com.example.my_app");
//var aux=Directory("/data/data/com.example.my_app/files");
var files = aux.listSync(recursive:true).toList();
print(files[2]);
for(var i=0; i<files.length; i++){
print(files[i]);
}
And the following code returns the paths below:
Directory tempDir = await getTemporaryDirectory(); // /data/user/0/com.example.my_app/cache
Directory appDocDir = await getApplicationDocumentsDirectory(); // /data/user/0/com.example.my_app/app_flutter
How can I locate the assets/my_file.py so that I can use it from the package (starflut)?
Thanks
Since your asset is packed with application you will have to copy the file from rootBundle to temp directory or your desired location. You can check out this medium post on how to read asset file and write to app path.
Hope this helps.