Save PDF in Xamarin Forms when targeting Android 11

dush 21 Reputation points
2021-12-17T09:14:09.607+00:00

I want a method to save PDF that coming as a byte[] from the server. iOS working well. In Android, I need to save the PDF in my app specific storage location. Previously I saved in the common external storage location. With the new android policy I cannot do that. All my updates are rejected from the play store. Is there a way to use Xamarin.Essential for save the PDF? Can we use Directory.CreateDirectory in Android 11 now?

Old code in Android side...

public class AndroidSaveFile : ISaveFile
    {
        public async Task<string> SaveFiles(string filename, byte[] bytes, string filePath)
        {
            var status = await CrossPermissions.Current.CheckPermissionStatusAsync<StoragePermission>();
            if (status != PermissionStatus.Granted)
            {
                if (await CrossPermissions.Current.ShouldShowRequestPermissionRationaleAsync(Permission.Storage))
                {
                    await Application.Current.MainPage.DisplayAlert("Storage permission!", "To save files, you need the storage permissions.", "OK");
                }

                status = await CrossPermissions.Current.RequestPermissionAsync<StoragePermission>();
            }

            if (status == PermissionStatus.Granted)
            {
                if (!Directory.Exists(filePath))
                {
                    Directory.CreateDirectory(filePath);
                }

                File.WriteAllBytes(filename, bytes);
            }
            else if (status != PermissionStatus.Unknown)
            {
                await Application.Current.MainPage.DisplayAlert("Storage Denied!", "Can not access storage on this device. Please check the permission and try again.", "OK");
            }

            return filename;
        }
    }

Then I tried using Xamarin.Essentials for saving and open PDF in the shared project. Still got reject.

  string dateTimePart = DateTime.Now.ToString("yyMMddHHmmss");
                        string fileName = "bonus_" + dateTimePart + ".pdf";
                        var filePath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);

                        if (Device.RuntimePlatform == Device.Android)
                        {
                            var file = Path.Combine(filePath, fileName);
                            File.WriteAllBytes(file, resList);

                            await Launcher.OpenAsync(new OpenFileRequest
                            {
                                Title = fileName,
                                File = new ReadOnlyFile(file)
                            });
                        }
Developer technologies .NET Xamarin
0 comments No comments
{count} votes

2 answers

Sort by: Most helpful
  1. Alessandro Caliaro 4,196 Reputation points
    2021-12-17T09:20:43.793+00:00

    file-system-helpers

    I think you can use AppDataDirectory to have you app directory, then create a subdir inside it


  2. JarvanZhang 23,971 Reputation points
    2021-12-20T08:51:39.767+00:00

    Starting in Android 11, apps cannot create their own app-specific directory on external storage. Try using MediaStore API to access media files from shared storage using direct file paths.

    var values = new ContentValues();
    
    values.Put(MediaStore.IMediaColumns.DisplayName, Path.GetFileNameWithoutExtension(fileName));
    values.Put(MediaStore.IMediaColumns.MimeType, MimeService.GetMimeType(Path.GetExtension(fileName)));
    values.Put(MediaStore.IMediaColumns.IsPending, true);
    
    var contentResolver = Android.App.Application.Context.ContentResolver;
    var collection = MediaStore.Downloads.GetContentUri(MediaStore.VolumeExternalPrimary);
    var fileUri = contentResolver.Insert(collection, values);
    using var stream = contentResolver.OpenFileDescriptor(fileUri, "w");
    using var pdfStream = new ParcelFileDescriptor.AutoCloseOutputStream(stream);
    
    await pdfStream.WriteAsync(bytes);
    

    Here is the related doc, you could refer to it: https://developer.android.com/about/versions/11/privacy/storage#app-specific-external

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.