Home > Mobile >  How to save a csv or txt in Xamarin UWP app?
How to save a csv or txt in Xamarin UWP app?

Time:05-28

Today I've got a question about saving a .csv or .txt file within a Xamarin app on UWP platform. I am trying to save a file I create in my code call tags.csv. My goal is to have no .csv's saved initially, I create an instance in my code, then save it and create a new .csv file when my code executes. The creation and filling of the .csv occurs in one function which triggers based on a Button instance in my app. Also, ideally I could make it save in a location determined by a file explorer popup.

I have tried two routes so far to make and save a .csv file, the CSVExport package and CSVhelper package. Both I have been able to download and add to my project from NuGet successfully.

I have tried separately a simple implementation of each, basically just taking their Example code to see if it would work in my UWP app. Here is the respective code

// CSVExport methods, two ways to save
var myExport = new CsvExport();
...
File(myExport.ExportToBytes(), "text/csv", "results.csv"); // method 1
myExport.ExportToFile("./results.csv");                    // method 2

// CSVhelper method
var records = new List<Foo>
{
   new Foo { Id = 1, Name = "one" },
};                
using (var writer = new StreamWriter("./tags.csv"))
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
{
   csv.WriteRecords(records);
}

Here is the error I am receiving: System.UnauthorizedAccessException: 'Access to the path C:...(filepath)...BLE.Client.UWP\bin\x86\Debug\AppX\tags.csv' is denied.'

Whenever the code reaches my saving of the .csv file, it crashes the app and Visual Studio 2022 gives me this error message. The same exact error occurs whether I am using CSVExport or CSVhelper.

Attempted Solutions:

My attempted solutions are mainly in regards to giving the app the permissions it needs to save. If an alternative like getting a different CSV package is better, I would take that advice too.

One solution I saw on StackOverflow linked to this page. The issue is I cannot load StorageFolder or Windows.Storage in my Xamarin app, it just won't recognize it and won't compile cause it's a missing load action.

Another solution I saw was changing your Capabilities in the Package.appxmanifest file and changing your Package header. I have done so, so mine looks like the following code sample. I need the internetClient and bluetooth and location for the app itself, so I added broadFilesystemaccess and even documents and pictures just to see if that would work too.

<Package
  xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
  xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
  xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
  IgnorableNamespaces="uap mp rescap">

 ...

<Capabilities>
  <Capability Name="internetClient" />
  <rescap:Capability Name="broadFileSystemAccess" />

  <uap:Capability Name="documentsLibrary"/>
  <uap:Capability Name="picturesLibrary" />

  <DeviceCapability Name="bluetooth" />
  <DeviceCapability Name="location"/>  
</Capabilities>

Another solution was making sure the UWP app had permissions, which I went into system settings and allowed, so it should have full access now.

I am not sure where to go from here, so any advice about UWP or saving files within Xamarin UWP apps would be appreciated.

CodePudding user response:

Based on your requirement, you could try to use the DependencyService feature of Xamarin.Forms.

DependencyService enables you to invoke native platform functionality from shared code. For your scenario, you could pass the stream of the file to the DependencyService first. then you could call the UWP FileSavePicker using DependencyService in your Forms app and save the stream as a file.

Here are some code snippets about how to implement the interface.

SaveFileAsync

        public async Task SaveFileAsync(Stream data)
    {
        FileSavePicker savePicker = new FileSavePicker();
        savePicker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
        // Dropdown of file types the user can save the file as
        savePicker.FileTypeChoices.Add("Text", new List<string>() { ".txt" });
        savePicker.FileTypeChoices.Add("CSV", new List<string>() { ".csv" });
        // Default file name if the user does not type one in or select a file to replace
        savePicker.SuggestedFileName = "New Document";

        StorageFile file = await savePicker.PickSaveFileAsync();
       
        if (file!= null) 
        {
            using (IRandomAccessStream dataStream= data.AsRandomAccessStream()) 
            {
                using (var reader = new DataReader(dataStream.GetInputStreamAt(0)))
                {
                    await reader.LoadAsync((uint)dataStream.Size);
                    var buffer = new byte[(int)dataStream.Size];
                    reader.ReadBytes(buffer);
                    await Windows.Storage.FileIO.WriteBytesAsync(file, buffer);
                }
            }
        }
    }

ISaveFileServicecs interface

 public interface ISaveFileServicecs
    {
        Task<Stream> SaveFileAsync(Stream stream);
    }

Usage

 await DependencyService.Get<ISaveFileServicecs>().SaveFileAsync(stream);
  • Related