How to read files from Android external storage in MAUI application - c#

How can I read/write all/specific files from an android external storage folder in a MAUI application. I know there are native ways to do that in Xamarin.Android but here I am looking for more modern abstract way to achieve it. Kindly help.
To be specific I am looking for some abstraction/API to list files on Android platform. something like below would be ideal or similar-
var files = Directory.GetFiles(<FolderPath>)
Any pointer would really help. Thanks a lot.

Take .txt file as an example.
MainPage.xaml:
...
<VerticalStackLayout>
<Button Text="File"
Clicked="file_clicked"/>
<Button Text="Read_file"
Clicked="Read_file"/>
</VerticalStackLayout>
In MainPage.xaml.cs, you can add this:
//string targetFile = System.IO.Path.Combine(FileSystem.Current.AppDataDirectory, "TextFile3.txt");
// Write the file content to the app data directory
private async void file_clicked(object sender, EventArgs e)
{
#if ANDROID
//var docsDirectory = Android.App.Application.Context.GetExternalFilesDir(Android.OS.Environment.DirectoryDocuments);
//File.WriteAllText($"{docsDirectory.AbsoluteFile.Path}/atextfile.txt", "contents are here");
var docsDirectory = Android.App.Application.Context.GetExternalFilesDir(Android.OS.Environment.DirectoryDcim);
File.WriteAllText($"{docsDirectory.AbsoluteFile.Path}/atextfile.txt", "contents are here");
#endif
//using FileStream outputStream = System.IO.File.OpenWrite(targetFile);
//using StreamWriter streamWriter = new StreamWriter(outputStream);
//await streamWriter.WriteAsync("ssss");
}
//read file
private async void Read_file(object sender, EventArgs e)
{
#if ANDROID
var docsDirectory = Android.App.Application.Context.GetExternalFilesDir(Android.OS.Environment.DirectoryDcim);
var a = File.OpenRead($"{docsDirectory.AbsoluteFile.Path}/atextfile.txt");
using StreamReader reader_1 = new StreamReader(a);
var doc = await reader_1.ReadToEndAsync();
await DisplayAlert("content", doc, "OK");
#endif
//using FileStream outputStream_2 = System.IO.File.OpenRead(targetFile);
//using StreamReader reader_2 = new StreamReader(outputStream_2);
//string content_2 = await reader_2.ReadToEndAsync();
//content_2 = content_2.ToUpperInvariant();
}

Related

Why SetSource cannot change the value of Source?

I set the original Source of MediaElement to be abc.mp3, and I'm trying to play another local mp3, like D://xxx.mp3.
When I use SetSource, I found that the value of Source was still abc.mp3, but the music actually changed to be xxx.mp3.
Can I use player.Source = "D://xxx.mp3"; ?
Here is my code:
//player is a MediaElement.
private async void Button_Click(object sender, RoutedEventArgs e)
{
await SetLocalMedia();
}
async private System.Threading.Tasks.Task SetLocalMedia()
{
var openPicker = new Windows.Storage.Pickers.FileOpenPicker();
openPicker.FileTypeFilter.Add(".wmv");
openPicker.FileTypeFilter.Add(".mp4");
openPicker.FileTypeFilter.Add(".wma");
openPicker.FileTypeFilter.Add(".mp3");
var file = await openPicker.PickSingleFileAsync();
if (file != null)
{
var stream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read);
Debug.WriteLine(player.Source);
player.SetSource(stream, file.ContentType);
Debug.WriteLine(player.Source);//The output of these tow Debug are same
}
}
Why SetSource cannot change the value of Source?
Thank you for your time to report this, the problem is that if you call the SetSource method to set media source with local file open stream, so the Source property will not contain value, and it is by design.
Can I use player.Source = "D://xxx.mp3"; ?
Source property is Uri type, it support http and UWP local uri scheme, if the file stored in D disk, you could not set the player.Source = "D://xxx.mp3(file scheme). If you do want to set the source with uri value, I suggest copy the file to the app's local folder then use UWP local file uri scheme. But it will cause the app's local storage becomes larger.
private async void Button_Tapped(object sender, TappedRoutedEventArgs e)
{
var openPicker = new Windows.Storage.Pickers.FileOpenPicker();
openPicker.FileTypeFilter.Add(".wmv");
openPicker.FileTypeFilter.Add(".mp4");
openPicker.FileTypeFilter.Add(".wma");
openPicker.FileTypeFilter.Add(".mp3");
var file = await openPicker.PickSingleFileAsync();
if (file != null)
{
await file.CopyAsync(ApplicationData.Current.LocalFolder, file.Name, NameCollisionOption.ReplaceExisting);
var path = ApplicationData.Current.LocalFolder.Path;
var stream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read);
Debug.WriteLine(player.Source);
player.Source = new Uri($"ms-appdata:///local/{file.Name}");
Debug.WriteLine(player.Source);//The output of these tow Debug are same
}
player.Play();
}

Upload file to Rest API in WindowsForms C#

I am able to upload the files to an API, but I need a small help. Right now I just hard-coded it. But actually, I will be having a PDF and XML files in two different local file storage locations, I need to get the files from that location and needs to upload them to API. Can anyone help me to achieve this?
private void btnsubmit_Click(object sender, EventArgs e)
{
UploadFileAsync(#"D:\test\SBP-1102.pdf");
}
public static async Task UploadFileAsync(string path)
{
HttpClient client = new HttpClient();
// we need to send a request with multipart/form-data
var multiForm = new MultipartFormDataContent();
// add file and directly upload it
FileStream fs = File.OpenRead(path);
multiForm.Add(new StreamContent(fs), "files", Path.GetFileName(path));
// send request to API
var url = "https://spaysaas-dev/api/getOCRDocuments";
var response = await client.PostAsync(url, multiForm);
if(response.IsSuccessStatusCode)
{
MessageBox.Show("Success");
}
else
{
MessageBox.Show(response.ToString());
}
}
This answer is incomplete in that it doesn't actually explain why the file isn't being uploaded, but it might help you diagnose the problem.
The documentation on WebClient.UploadFileAsync says:
The file is sent asynchronously using thread resources that are automatically allocated from the thread pool. To receive notification when the file upload completes, add an event handler to the UploadFileCompleted event.
So you could try handling WebClient.UploadFileCompleted and checking the UploadFileCompletedEventArgs for errors.
private void Upload(string fileName)
{
var client = new WebClient();
client.UploadFileCompleted += Client_UploadFileCompleted;
try
{
var uri = new Uri("https://saas-dev/api/getDocs");
{
client.Headers.Add("fileName", System.IO.Path.GetFileName(fileName));
client.UploadFileAsync(uri, fileName);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void Client_UploadFileCompleted(object sender, UploadFileCompletedEventArgs e)
{
// Check e.Error for errors
}
I am able to upload the PDF file to an API using multi-part form data

Save string to xml file (UWP) c#

I am trying to save an string to an xml file and am recieving the following error:
An exception of type 'System.UnauthorizedAccessException' occurred in System.IO.FileSystem.dll but was not handled in user code
Additional information: Access to the path 'c:\users\brandon\documents\visual studio 2015\Projects\UniversalTestApp\UniversalTestApp\bin\x86\Debug\AppX\xmlfile.xml' is denied.
Reading further into the issue it would seem that I don't have the approriate permissions to complete this save. How would I go about
private async void btnSubmit_Click(object sender, RoutedEventArgs e)
{
var bookmark = new Bookmark();
bookmark.Button = cmbButton.SelectedIndex;
bookmark.Name = txtName.Text;
bookmark.URL = txtURL.Text;
string output = SerializeToXml(bookmark);
XmlDocument xdox = new XmlDocument();
File.WriteAllText("xmlfile.xml",output);
}
As it turns out the reason for the error message is that you can only access certain file system locations by default in UWP. I made some modifications to my code and it is now working correctly. Thanks to those who tried to help.
private async void btnSubmit_Click(object sender, RoutedEventArgs e)
{
var bookmark = new Bookmark();
StorageFolder storageFolder = ApplicationData.Current.LocalFolder;
StorageFile createFile = await storageFolder.CreateFileAsync("bookmark.xml", CreationCollisionOption.ReplaceExisting);
bookmark.Button = cmbButton.SelectedIndex;
bookmark.Name = txtName.Text;
bookmark.URL = txtURL.Text;
var output = SerializeToXml(bookmark);
StorageFile sampleFile = await storageFolder.GetFileAsync("bookmark.xml");
await FileIO.WriteTextAsync(sampleFile, output);
}
Try to run your application "as administrator"

System.Runtime.InteropServices.COMException occurred in mscorlib.ni.dll but was not handled in user code

I am developing a windows application in 8.1 and I am getting a following error.
my application includes a procedure in which I will be moving a file from local storage to SD card.
My code is as follows
namespace MoveFile
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
}
private async void btnCreateFolder_Click(object sender, RoutedEventArgs e)
{
await ReadFile();
//Error is showing here
**await WriteToFile();
}
public async Task WriteToFile()
{
// Get the text data from the textbox.
byte[] fileBytes = System.Text.Encoding.UTF8.GetBytes(this.txtSafakCount.Text.ToCharArray());
//I got the error in this line.....showing interopservice exception
** StorageFolder knownFolder = await KnownFolders.RemovableDevices.CreateFolderAsync("bdfbdfb", CreationCollisionOption.ReplaceExisting);
StorageFolder sdCard = (await knownFolder.GetFoldersAsync()).FirstOrDefault();
// Create a new file named DataFile.txt.
var file = await sdCard.CreateFileAsync("kaaaaammmmfewfwmHoJa.txt", CreationCollisionOption.ReplaceExisting);
// Write the data from the textbox.
using (var s = await file.OpenStreamForWriteAsync())
{
s.Write(fileBytes, 0, fileBytes.Length);
}
}
public async Task ReadFile()
{
// Get the local folder.
StorageFolder local = Windows.Storage.ApplicationData.Current.LocalFolder;
if (local != null)
{
// Get the DataFolder folder.
var dataFolder = await local.GetFolderAsync("DataFolder");
// Get the file.
await dataFolder.CreateFileAsync("DataFile.txt", CreationCollisionOption.ReplaceExisting);
var file = await dataFolder.OpenStreamForReadAsync("DataFile.txt");
// Read the data.
using (StreamReader streamReader = new StreamReader(file))
{
this.txtSafakCount.Text = streamReader.ReadToEnd();
}
}
}
}
}
I want to know why this exception occurred and how it can be resolved.
Thanks in advance.
You are doing it wrong - first you should get SD card, then create folder. As the name KnownFolders.RemovableDevices says devices (not single device) - so you get the first of them as SD card (note that Universal Apps are not only for phones). So the working code can look like this:
// first get the SD card
StorageFolder sdCard = (await KnownFolders.RemovableDevices.GetFoldersAsync()).FirstOrDefault();
// then perform some actions - create folders, files ...
StorageFolder myFolder = await sdCard.CreateFolderAsync("bdfbdfb", CreationCollisionOption.ReplaceExisting);
Note also that you also need to add Capabilities in package.appxmanifest file, and Declarations if you want to use files (File Type Associations).
You will also find more help at MSDN.

How can I write to a specific location in Windows Phone 8

When I press a button, I want it to overwrite a file to a specific folder.
I use this code:
private void btnArial_Click(object sender, RoutedEventArgs e)
{
string cssDocument = "body{font-family:\"Arial\";}";
//I want to write file style.css to folder css inside html
string path = Package.Current.InstalledLocation.Path + "\\Html\\css\\style.css";
if (File.Exists(path))
{
StreamWriter writer = new StreamWriter(path);
writer.Write(cssDocument);
writer.Close();
}
changeStyle(new FontFamily("Arial"));
}
When I tested on emulator and actual devide, it worked properly.
But when I submit app to store, it got error - the app exits when I press that button.
The install directory (Package.Current.InstalledLocation) is a read-only location. Unfortunately, due to the way that Visual Studio optimizes development-time deployment, it is set to read-write when the app is deployed from VS. That's why you see a difference in behavior after you submit the app to the store.
If you need to modify a file in your install directory, you must first copy it over to a writeable location - eg. your Local folder.
I prefer using Isolated storage in WP8 to write files and it never fails. Also you can use Windows.Storage apis.
private async void MyButton_Click(object sender, RoutedEventArgs e)
{
string cssDocument = "body{font-family:\"Arial\";}";
// using Windows.Storage
StorageFolder folder = ApplicationData.Current.LocalFolder;
folder = await folder.CreateFolderAsync("HTML", CreationCollisionOption.OpenIfExists);
folder = await folder.CreateFolderAsync("CSS", CreationCollisionOption.OpenIfExists);
StorageFile file = await folder.CreateFileAsync("style.css", CreationCollisionOption.ReplaceExisting);
using (var writer = new StreamWriter(await file.OpenStreamForWriteAsync()))
{
writer.Write(cssDocument);
}
// using using System.IO.IsolatedStorage;
using (var store = IsolatedStorageFile.GetUserStoreForApplication())
{
if (!store.DirectoryExists("HTML/CSS"))
store.CreateDirectory("HTML/CSS");
using (var writer = new StreamWriter(store.OpenFile("HTML/CSS/style.css", FileMode.Create)))
{
writer.Write(cssDocument);
}
}
changeStyle(new FontFamily("Arial"));
}
Exactly..
Write the file in Isolated storage. Its easier and pretty straight forward. The files here can be accessed, viewed, modified, removed, replaced in a very clear way. I personally prefer the Isolated Storage.

Categories