Forum Xamarin.Forms

Write file to public directory (Android)

Good afternoon people.

I am trying to save files to a public directory but so far without success. I already saved and recovered internally in the app without any problems, but I wanted to save it in a public directory.

I use Xamarin Forms, I am developing an app for Android and iOS similar to WhatsApp. I wanted to save in a public directory for two reasons.

1- To be like WhatsApp that creates a directory at the root with all media files, it is easy for the user to access that directory, backup, clean and etc.

2- I am using Xam.Plugin.SimpleAudioPlayer (https://github.com/adrianstevens/Xamarin-Plugins/issues/75) to play the audio files, it works very well, but on devices with Android 10 it is not playing, error (Prepare failed .: status = 0x1) when trying to play the file. I imagine it is because of the new permissions on Android 10. If the file were in a public directory it would probably work.

Anyway, I've tried everything to save a file in a public directory and so far I haven't been able to. Does anyone know how to do this?

Below are some of the tests I've done via DependencyService:

Best Answer

  • LeonLuLeonLu Member, Xamarin Team Xamurai
    Accepted Answer

    I wirte a dependence Service to achieve it. I notice you want to save the file to the download folder.

    Here is running GIF.

    I create an interface to achieve it in PCL.

      public interface ISave
        {
            void SaveAndView();
        }
    

    In the android platform, I achieve following it with following code. I wirte my pdf in the Assest folder to the outside the download folder.

    [assembly: Dependency(typeof(SaveService))]
    namespace App37.Droid
    {
        class SaveService : ISave
        {
            public void SaveAndView()
            {
    
                string root = null;
                //Get the root path in android device.
                if (Android.OS.Environment.IsExternalStorageEmulated)
                {
    
                    root = Path.Combine(Android.OS.Environment.ExternalStorageDirectory.AbsolutePath, Android.OS.Environment.DirectoryDownloads);
    
                }
                else
                    root = System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments);
    
                //Create directory and file 
                Java.IO.File myDir = new Java.IO.File(root + "/meusarquivos");
                myDir.Mkdir();
                string fileName = "test.pdf";
                Java.IO.File file = new Java.IO.File(myDir, fileName);
    
                //Remove if the file exists
                if (file.Exists()) file.Delete();
    
                AssetManager assets = Android.App.Application.Context.Assets;
    
                var bytes = default(byte[]);
                using (StreamReader reader = new StreamReader(assets.Open("test.pdf")))
                {
                    using (var memstream = new MemoryStream())
                    {
                        reader.BaseStream.CopyTo(memstream);
                        bytes = memstream.ToArray();
                    }
                }
    
    
                //Write the stream into the file
                FileOutputStream outs = new FileOutputStream(file);
                outs.Write(bytes);
    
                outs.Flush();
                outs.Close();
            }
        }
    }
    

    Please add the following permission in the AndroidManifest.xml

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    

    And request the runtime permission, before you save the file. I make a testing, So I add the request the runtime permission in the MainActivity.cs

      public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
        {
            protected override void OnCreate(Bundle savedInstanceState)
            {
                TabLayoutResource = Resource.Layout.Tabbar;
                ToolbarResource = Resource.Layout.Toolbar;
    
                base.OnCreate(savedInstanceState);
                Rg.Plugins.Popup.Popup.Init(this, savedInstanceState);
                Xamarin.Essentials.Platform.Init(this, savedInstanceState);
                global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
                //access the wirte permission.
                if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.WriteExternalStorage) != (int)Permission.Granted)
                {
                    RequestPermissions(new string[] { Manifest.Permission.ReadExternalStorage, Manifest.Permission.WriteExternalStorage }, 0);
                }
    
                LoadApplication(new App());
            }
    
    

Answers

  • LeonLuLeonLu Member, Xamarin Team Xamurai
    Accepted Answer

    I wirte a dependence Service to achieve it. I notice you want to save the file to the download folder.

    Here is running GIF.

    I create an interface to achieve it in PCL.

      public interface ISave
        {
            void SaveAndView();
        }
    

    In the android platform, I achieve following it with following code. I wirte my pdf in the Assest folder to the outside the download folder.

    [assembly: Dependency(typeof(SaveService))]
    namespace App37.Droid
    {
        class SaveService : ISave
        {
            public void SaveAndView()
            {
    
                string root = null;
                //Get the root path in android device.
                if (Android.OS.Environment.IsExternalStorageEmulated)
                {
    
                    root = Path.Combine(Android.OS.Environment.ExternalStorageDirectory.AbsolutePath, Android.OS.Environment.DirectoryDownloads);
    
                }
                else
                    root = System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments);
    
                //Create directory and file 
                Java.IO.File myDir = new Java.IO.File(root + "/meusarquivos");
                myDir.Mkdir();
                string fileName = "test.pdf";
                Java.IO.File file = new Java.IO.File(myDir, fileName);
    
                //Remove if the file exists
                if (file.Exists()) file.Delete();
    
                AssetManager assets = Android.App.Application.Context.Assets;
    
                var bytes = default(byte[]);
                using (StreamReader reader = new StreamReader(assets.Open("test.pdf")))
                {
                    using (var memstream = new MemoryStream())
                    {
                        reader.BaseStream.CopyTo(memstream);
                        bytes = memstream.ToArray();
                    }
                }
    
    
                //Write the stream into the file
                FileOutputStream outs = new FileOutputStream(file);
                outs.Write(bytes);
    
                outs.Flush();
                outs.Close();
            }
        }
    }
    

    Please add the following permission in the AndroidManifest.xml

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    

    And request the runtime permission, before you save the file. I make a testing, So I add the request the runtime permission in the MainActivity.cs

      public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
        {
            protected override void OnCreate(Bundle savedInstanceState)
            {
                TabLayoutResource = Resource.Layout.Tabbar;
                ToolbarResource = Resource.Layout.Toolbar;
    
                base.OnCreate(savedInstanceState);
                Rg.Plugins.Popup.Popup.Init(this, savedInstanceState);
                Xamarin.Essentials.Platform.Init(this, savedInstanceState);
                global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
                //access the wirte permission.
                if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.WriteExternalStorage) != (int)Permission.Granted)
                {
                    RequestPermissions(new string[] { Manifest.Permission.ReadExternalStorage, Manifest.Permission.WriteExternalStorage }, 0);
                }
    
                LoadApplication(new App());
            }
    
    
  • RodrigoGimenezRodrigoGimenez USMember ✭✭
    edited September 2020

    Thank you very much for your answer LeonLu, in reality what was missing for me was:

    if (ContextCompat.CheckSelfPermission (this, Manifest.Permission.WriteExternalStorage)! = (int) Permission.Granted) { RequestPermissions (new string [] {Manifest.Permission.ReadExternalStorage, Manifest.Permission.WriteExternalStorage}, 0); }

    I added this permission and it worked to save files external to the App.

    Regarding item 2 the problem continues, in Android 10 I can't play the audio using the Xam.Plugin.SimpleAudioPlayer plugin, I think the problem is with the plugin.

    Thank you anyway.

  • harrycallumharrycallum Member ✭✭

    thank you so much for sharing this info and I also would like to know can I do this directory method through this FMWhatsapp latest version which I have downloaded to use as dual whatsapp.

  • j_55j_55 Member ✭✭

    such a great deal thanks for sharing

  • j_55j_55 Member ✭✭
    edited December 2020

    ok, download it to enjoy

  • j_55j_55 Member ✭✭

    3

  • j_55j_55 Member ✭✭

    44

  • j_55j_55 Member ✭✭

    5

  • gonowtechgonowtech Member

    Thanks for sharing this much info in this forum.

  • BabanaqashBabanaqash Member

    Thank you for sharing this info

Sign In or Register to comment.