My MauiBlazorApp can create a .pdf file. In what ways can I make this file accessible for the users of windows/android/IOS. A SaveFile Dialog is not yet implemented in Maui. With this path
string SaveToPath = System.IO.Path.Combine(FileSystem.Current.AppDataDirectory, "hello.pdf");
I can save it on a windows machine and open it (tested).
The path (FileSystem.Current.AppDataDirectory) on a local android device (phone connected to windows maschine via usb) is something like this:
/data/user/0/com.companyname.appname/files/hello.pdf
But later I can't find this folder on my phone. I find this:
/data/com.companyname.appname/cache/ empty folders all the way
Why can't I save a file in .../downloads
How do you import/export data from 'device to device' in Maui? Files seems to be not the way. Email with attechments? Is that possible/better?
You can check the Maui File picker, It provides the method you can pick the file from the device.
public async Task<FileResult> PickAndShow(PickOptions options)
{
try
{
var result = await FilePicker.Default.PickAsync(options);
if (result != null)
{
if (result.FileName.EndsWith("jpg", StringComparison.OrdinalIgnoreCase) ||
result.FileName.EndsWith("png", StringComparison.OrdinalIgnoreCase))
{
using var stream = await result.OpenReadAsync();
var image = ImageSource.FromStream(() => stream);
}
}
return result;
}
catch (Exception ex)
{
// The user canceled or something went wrong
}
return null;
}
In addition, you can refer to Folder Picker .NET MAUI. This is more detailed.
Related
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
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()
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))))
}
Using code to Preserve access to a StorageFolder stolen from Karl Erickson
public static async Task<StorageFolder> GetOrPickAndRememberFolderAsync(string key)
{
if (StorageApplicationPermissions.FutureAccessList.ContainsItem(key))
{
return await StorageApplicationPermissions.FutureAccessList.GetFolderAsync(key);
}
var picker = new FolderPicker();
picker.FileTypeFilter.Add("*");
StorageFolder folder = await picker.PickSingleFolderAsync();
if (folder != null)
{
StorageApplicationPermissions.FutureAccessList.AddOrReplace(key, folder);
}
return folder;
}
our UWP app is able to browse to and remember the location of a SQLite database chosen by a user. This should give read/write permissions on that folder.
The dbContext is instantiated like this
public dbContext(string dbPath)
{
databasePath = dbPath;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite("Filename=" + databasePath);
}
This code works if the user chooses the default local storage location for the app.
var picker = new FolderPicker();
picker.FileTypeFilter.Add("*");
StorageFolder folder = await picker.PickSingleFolderAsync();
StorageFolder dbFolder = await GetOrPickAndRememberFolderAsync("LocalDbFolder");
string dbPath = Path.Combine(dbFolder.Path, "MySQLite.db");
using (var db = new dbContext(dbPath))
{
db.Database.Migrate();
}
However if the user chooses any other location (such as their Documents folder) the app fails with the error "SQLite Error 14: 'unable to open database file'."
Why would that be, given the user has given the app explicit permissions on the selected location through the picker?
You can't.
SQLite requires native file access. To use the native file access, you should put the sqlite .db file to the application local folders - Windows.Storage.ApplicationData.Current.LocalFolder or tempfolder.
If you use the file/folder picker, you can get the storagefolder item. But, if the folder is outside of your application, ALL of file accesses are done with OS's broker process. SQLite can't access the such of folders.
I need to upload an image to a server, less then 20 lines of code please. and no questions about services. My boss expects it to work like ftp of old days...
I don't think there is a 20 lines solution to your problem.
But check this link out :
http://code.msdn.microsoft.com/Windows-8-SocketsFtp-4fc23b33#content
It contains a full Ftp client that works on Windows 8.1 metro application.
You could use this project as a library in your own project.
If you especially need to upload file, I think you need to use the UploadFileAsync function in FtpClient class
public async Task UploadFileAsync(StorageFile file, string destination)
{
using (var stream = await OpenWriteAsync(destination))
{
//
// A more efficient way, maybe a DataReader can be used here
using (var readStream = await file.OpenReadAsync())
{
var buffer = new byte[512].AsBuffer();
var resultingBuffer = new byte[0];
while (true)
{
IBuffer readBuffer = await readStream.ReadAsync(buffer, 512, InputStreamOptions.Partial);
if (readBuffer.Length == 0) break;
resultingBuffer = resultingBuffer.Concat(readBuffer.ToArray()).ToArray();
}
await stream.WriteAsync(resultingBuffer.AsBuffer());
await stream.FlushAsync();
}
}
}