Flutter create a file and write its contents - flutter

I am working on an app which gathers text content from server and as the next step I am trying to save them into separate files to be saved with in apps storage. Storage is a folder called 'storage'. Inside storage, there is a 'fileList.txt' file which contains the list of files.
In pubspec.yaml I declared this folder in the assets as follows:
assets:
- storage/
And I can read it using the following function:
Future<String> readFileContent() async {
String response;
response =
await rootBundle.loadString('storage/fileList.txt');
return response;
}
To save the file I made use of online examples which use 'path_provider' package. However when I try to save one of the downloaded files for instance 'file4.dat' using the following functions, I encounter the following error. How can I resolve that problem?
saveFileLocally() async {
await _writeFile();
}
Future<Null> _writeFile() async {
await (await _getLocalFile()).writeAsString(vars.fileContents);
}
Future<File> _getLocalFile() async {
// get the path to the document directory.
String dir = (await PathProvider.getApplicationSupportDirectory()).path;
print('DIR :::: ' + dir);
return new File(
'$dir/storage/files/file' + vars.newFileID.toString() + '.dat');
}
The error is:
Unhandled Exception: FileSystemException: Cannot open file,
path = '/data/user/0/com.example.ferenova_flutter_app/files/storage/file4.dat'
(OS Error: No such file or directory, errno = 2)
Thanks guys and take care !!! :)

While checking out path_provider examples, I ran into this important detail:
Directory directory = Platform.isAndroid
? await getExternalStorageDirectory() //FOR ANDROID
: await getApplicationSupportDirectory(); //FOR iOS
This solved my problem. I am no longer using default rootBundle asset to process any external files.

I think you should check your paths.
storage/fileList.txt is not the same as $dir/storage/files/file' + vars.loadedFileID.toString() + '.dat'
you should try:
response =
await rootBundle.loadString('$dir/storage/files/file' + vars.loadedFileID.toString() + '.dat'');
Or something similar.
GL

Related

Writing to file in Flutter multiple times updates the file. Reading from the file always gives me the initial content

SOLVED FOR MY SITUATION. MORE INFORMATION HERE AND THEN ORIGINAL QUESTION BELOW.
===Solution===
Due to settings with Android external storage, the file_picker plugin creates a cache of the file you pick and stores it in a cache directory within the app storage location. It will not overwrite this for files with the same name on subsequent reads. So for my read/write app, the solution was to do await file.delete(); when I was done with the read operation. This ensures that the next read will then create a cached version with the updated contents
===Original Question===
I have some content in a database on a Flutter app I am using to just practice some new stuff in FLutter. I have an export button that gets this data, JSON encodes it, and writes it to a file.
If I change the content and then export a second time, I can open the file on my device and see the updated content. I also have an import button. When I press that, I use FilePicker to select a file, read the contents of the file, and then JSON decode the data into an object.
I print out the file.readAsString and see the content from the initial write.
If I manually delete the file between writes then it works. If I use file.delete() before the write, it does not work. What can I do to get the updated text when I read from the file?
Getting file to write to. (I am aware this will only work on Android as is and that's fine)
Future<File?> _getBackupDataFile(String pathToTryFirst, ExportData data) async {
Directory? directory = Directory(pathToTryFirst);
if (!await directory.exists()) directory = await getExternalStorageDirectory();
if ((await directory?.exists() ?? false) == false) {
showErrorDialog(context: context, body: "Unable to find directory to save file.");
return null;
}
return File("${directory?.path}/pm-account-backup.json");
}
Write to file as such (without the delete code):
Future<void> _writeDataToFile(ExportData data) async {
try {
File? file = await _getBackupDataFile('/storage/emulated/0/Download', data);
if(file == null) { return; }
await file.writeAsString(jsonEncode(data));
await showSuccessDialog(context: context, title: "Success", body: "${data.accounts.length} accounts backed up successfully.");
} catch (e) {
showErrorDialog(context: context, body: "Failed to write data to file.");
}
}
Simplified file pick:
FilePickerResult? result = await FilePicker.platform.pickFiles();
if (result != null) {
String path = result.files.single.path ?? '';
if((path).endsWith(".json")) {
return File(path);
}
}
Read from file as such:
String fileData = await file.readAsString();
print(fileData);
Solution for my question found after more information provided by #pskink
Due to settings with Android external storage, the file_picker plugin creates a cache of the file you pick and stores it in a cache directory within the app storage location. It will not overwrite this for files with the same name on subsequent reads. So for my read/write app, the solution was to do await file.delete(); when I was done with the read operation. This ensures that the next read will then create a cached version with the updated contents

Flutter: moving files from local machine

Can you use flutter to move files from local machine? For example from C:\images\photo.png to C:\photos\photo.png?
Yes you can make files moving using Dart/Flutter, you need to import dart:io :
import "dart:io";
then you can use this method, you can understand what it does with the comments I wrote.
Future<File> moveFile(File originalFile, String targetPath) async {
try {
// This will try first to just rename the file if they are on the same directory,
return await originalFile.rename(targetPath);
} on FileSystemException catch (e) {
// if the rename method fails, it will copy the original file to the new directory and then delete the original file
final newFileInTargetPath = await originalFile.copy(targetPath);
await originalFile.delete();
return newFileInTargetPath;
}
}
final file = File("C:/images/photo.png");
final path = "C:/photos/";
await moveFile(file, path);
However, I will explain here what it does :
If your file is under the same path directory, then there is no need to move it, just rename them with the rename() method will work, if the file is in an else directory on your system, it will create a new File where it will copy that file to that path, Now we will have two copies of that File, one under the old path and the other under the new path, so we need to delete the old one with the delete() method, finally we returned the new file with return newFile;

File stored in app document directory always exists although it hasn't been created (Flutter)

I am currently developing a mobile app with flutter and I want to display the profile picture of a user and their friends.
The pictures are stored in Firebase Storage, but to minimize the number of requests I want to load each image once and then store it locally.
I wrote a function to get the image from Firebase storage and store it in the app document directory, but I can't figure out how to execute the function only when the file doesn't already exist locally.
This is an excerpt of my code
Future<File?> getProfilePic(String? uid) async {
Directory appDocumentsDirectory = await getApplicationDocumentsDirectory();
String filepath = "${appDocumentsDirectory.path}/profiles/$uid.jpg";
if (await File(filepath).exists()) {
return File(filepath);
} else {
var success = await downloadFromFirebaseAndSaveToLocal(
"profilepic/$uid.jpg", filepath);
if (success) {
getProfilePic(uid);
}
}
return null;
}
Somehow the condition of the if statement (await File(filepath).exists()) is always true. I guess it makes sense. I think the file exists in the directory but doesn't have any content.
Does anyone know how to check if the file has content?
A normal null check doesn't work.
Thank you for helping!
To check the size of the file, you can
if (File(filepath).lengthSync() != 0)
or
You can await the file.length()

FileSystemException: Cannot open file, path = 'storage/emulated/0/DCIM/docs/myPdf.pdf' (OS Error: Operation not permitted, errno = 1)

I am trying to create a pdf document and write my data in that file. The data is a list of int or uint type. Able to do the same for images but not pdf or doc file. All permissions are given and it works for images. My code is below -
Future <File> createDocFile(Uint8List fileData, String type) async
{
File file = new File(".pdf");
if(await PermissionHandler.checkPermission(Permission.storage)!=true){
Url.toastShow("Storage permission not found.");
await Permission.storage.request();
}
else {
await file.writeAsBytes(fileData);
print("PDF SAVED IN DEVICE");
Url.toastShow("PDF saved in device",Colors.green);
}
}
Whenever you create a new file the device can't automatically find it. You'd have to manually tell the device to refresh for the files. Before you'd have to refresh all the files in the device for it to get updated which was very inefficient, but now you could just send the path of the file that you want to get updated. You can use the media_scanner plugin to do so.
Or If you wanna do it yourself with kotlin then here's the code,
private fun broadcastFileUpdate(path: String){
context.sendBroadcast(Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,Uri.fromFile(File(path))))
}

Copying .sqlite db to documents directory on Phone

I have a routine that copies a local SQLite database (from the Android Studio Assets folder) to the phone's document directory.
The problem I have is that this code is not very good. Sometimes the database is copied completely, and sometimes it is copied partially, or not at all. Its very buggy and I have no idea on how to improve on it.
Many times I get an error saying that certain tables cannot be found.
Here is the code that Im using:
copyDB() async
{
// Construct a file path to copy database to
Directory documentsDirectory = await getApplicationDocumentsDirectory();
String path = join(documentsDirectory.path, DBAssistanceClass.databaseName);
print('The DB path is: '+ path);
// Only copy if the database doesn't exist
if (FileSystemEntity.typeSync(path) == FileSystemEntityType.notFound)
{
try
{
print('Copying DB...');
// Load database from asset and copy
ByteData data = await rootBundle.load(
join('assets', DBAssistanceClass.databaseName));
List<int> bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
// Save copied asset to documents
await File(path).writeAsBytes(bytes);
}
catch (error)
{
print(error);
}
}
}
This is for flutter, and I cannot find anything at all in dart documentation to assist.
I think if it is important to flush when you write the file. There is an Opening an asset database document in sqflite with a complete example.