How can I specify a custom extension with Xamarin.Essentials.FilePicker.PickAsync()?

Jesse Knott 691 Reputation points
2022-03-06T18:19:07.57+00:00

In my app I have the ability to save, export, and restore a sqlite database.
When the user exports the database it is saved as MyFile.db3 in the app's own storage folder under the public Documents folder Documents/MyApp
When the user restores a database to the app, in the past I just alerted the user to store the file in a specific location, then worked with those static paths. I would like to move away from this and allow the user to browse for the database to restore.

Here is the code I have for now.

    private async void BtnRestoreDatbase_Clicked(object sender, EventArgs e)
    {
        try
        {
            connection = DBHelper.CreateDBConnection();
            SQLiteAsyncConnection toMerge;


            var po = new PickOptions();

            FilePickerFileType dbFilePicker =
            new FilePickerFileType(new Dictionary<DevicePlatform, IEnumerable<string>>
            {
                             { DevicePlatform.iOS, new[] { "*.db3" } },
                             { DevicePlatform.Android, new[] { "*.db3" } },
            });

            po.FileTypes = dbFilePicker ;
            po.PickerTitle = "Select Database to restore";

            var RestoreDB = await Xamarin.Essentials.FilePicker.PickAsync(po);

            if( RestoreDB == null)
            {
                return;
            }
      }

The problem I am having is that the database file is showing up greyed out, and cannot be selected.
How would I specify the filter for this to select the database files with the extension of db3?

Additionally I use xml files as a way to export individual records, so it would be good to know what filter I need to specify for xml files too.

Cheers!

Developer technologies .NET Xamarin
0 comments No comments
{count} votes

Accepted answer
  1. Yonglun Liu (Shanghai Wicresoft Co,.Ltd.) 50,126 Reputation points Microsoft External Staff
    2022-03-07T06:19:06.883+00:00

    Hello,

    { DevicePlatform.Android, new[] { "*.db3" } },

    On Android platform, The FilePicker should use MIME type to find files with specific extension, such as:

    { DevicePlatform.Android, new[] { "application/comics" } },

    You can refer to the MIME documentation to find all MIME types.

    But the .db3 extension has no MIME type right now.

    There are two solution to solve your issue:

    Solution 1:

    You can use SQLite file with .db or .sqlite extension instead. They have the same MIME type known as application/vnd.sqlite3

    { DevicePlatform.Android, new[] { "application/vnd.sqlite3" } },

    Solution 2:

    Referring to the official documentation, you can use the following code in your try block:

       var result = await FilePicker.PickAsync();  
       if (result != null)  
       {  
       	var Text = $"File Name: {result.FileName}";  
       	if (result.FileName.EndsWith("db3", StringComparison.OrdinalIgnoreCase))  
       	{  
       		// Process the db3 file  
       	} else  
       	{  
       		DisplayAlert("error","Please choose .db3 file","cancel");  
       	}  
       }  
    

    Best Regards,

    Alec Liu.


    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.


3 additional answers

Sort by: Most helpful
  1. Jesse Knott 691 Reputation points
    2022-03-07T08:44:06.48+00:00

    @Yonglun Liu (Shanghai Wicresoft Co,.Ltd.) I think that renaming the extension would be easiest, I can make the app use .db or .sqlite form here on out, and add do some fancy footwork to migrate older database files that need to be renamed.
    I have set the proper MIME type "application/vnd.sqlite3", and I have tried renaming the file to both .db, and .sqlite. It seems that no matter what, I can see the file in the picker, but it's greyed out and I cannot select the file?

    I am trying to make this code reusable so I created this wrapper class that I call elsewhere in my code to find the file. This class is a bit ugly at the moment, but from what I can tell it should be working as expected. I've stepped through it line by line and it appears to be setting the MIME type and executing the options appropriately.

    class ImportFileDialog  
    {  
          
        /// <summary>  
        /// private storage Options  
        /// </summary>  
        private PickOptions options;  
          
        /// <summary>  
        /// Gets or sets the value for Options  
        /// </summary>  
        public PickOptions Options  
        {  
            get => options;  
          
            set  
            {  
        		options = value;  
            }  
        }  
          
        /// <summary>  
        /// private storage FileTypes  
        /// </summary>  
        private FilePickerFileType fileTypes;  
          
        /// <summary>  
        /// Gets or sets the value for FileTypes  
        /// </summary>  
        public FilePickerFileType FileTypes  
        {  
            get => fileTypes;  
          
            set  
            {  
        		fileTypes = value;  
            }  
        }  
          
        /// <summary>  
        /// private storage ResultingFile  
        /// </summary>  
        private FileResult resultingFile;  
          
        /// <summary>  
        /// Gets or sets the value for ResultingFile  
        /// </summary>  
        public FileResult ResultingFile  
        {  
            get => resultingFile;  
          
            set  
            {  
        		resultingFile = value;  
            }  
        }  
    
        /// <summary>  
        /// private storage Title  
        /// </summary>  
        private String title;  
          
        /// <summary>     
        /// Gets or sets the value for Title  
        /// </summary>  
        public String Title  
        {  
            get => title ?? string.Empty;  
          
            set  
            {  
        		title = value;  
            }  
        }  
    
        public ImportFileDialog(String incoming)  
        {  
            Title = incoming;  
            InitClass();  
        }  
    
        public ImportFileDialog()  
        {  
            InitClass();  
        }  
                 
    
        private void InitClass()  
        {  
            Options = new PickOptions();  
            Title = "Select File";  
        }  
    
        public void SetDBFile()  
        {  
            FileTypes = new FilePickerFileType(new Dictionary<DevicePlatform, IEnumerable<string>>  
            {  
               // { DevicePlatform.iOS, new[] { ".db3" } },  
                { DevicePlatform.Android, new[] { "application/vnd.sqlite3" } },  
               // { DevicePlatform.UWP, new[] { ".db3" } },  
            });  
        }  
    
    
        public void SetXMLFile()  
        {  
            FileTypes = new FilePickerFileType(new Dictionary<DevicePlatform, IEnumerable<string>>  
            {  
                { DevicePlatform.iOS, new[] { ".xml" } },  
                { DevicePlatform.Android, new[] { ".xml" } },  
                { DevicePlatform.UWP, new[] { ".xml" } },  
            });  
        }  
    
    
        public async Task<FileResult> getFile()  
        {  
            Options.FileTypes = FileTypes;  
            Options.PickerTitle = Title;  
    
            ResultingFile = await Xamarin.Essentials.FilePicker.PickAsync(Options);  
    
            if (ResultingFile == null)  
            {  
                return null;  
            }  
            else  
            {  
                return ResultingFile;  
            }  
        }  
    }  
    

    And my code that consumes this class is this .

                ImportFileDialog ifd = new ImportFileDialog();  
                ifd.Title = "Import Database";  
                ifd.SetDBFile();  
                var incomingFile = await ifd.getFile();  
    

    I'll try some more testing in depth tomorrow, but wanted to see if there were something simple I am missing.

    Thanks again!


  2. Attila Vlacsil 1 Reputation point
    2022-07-26T07:13:02.077+00:00

    I've found way to make it work with MAUI FilePicker (tested on Android 12.0), but the approach should be the same with Xamarin:

    Put an IntentFilter to your MainActivity:

    [IntentFilter(new[] { global::Android.Content.Intent.ActionView },    
    
        Categories = new[] { global::Android.Content.Intent.CategoryDefault, global::Android.Content.Intent.CategoryBrowsable },  
        DataScheme = "content",  
        DataHost = "*",  
        DataMimeTypes = new[] { "*/*", "application/foo", "application/octet-stream" },  
        DataPathPatterns = new[] { ".*\\.foo", ".*\\..*\\.foo", ".*\\..*\\..*\\.foo", ".*\\..*\\..*\\..*\\.foo", ".*\\..*\\..*\\..*\\..*\\.foo" })]  
    

    Then you can specify the custom type like this:

    var customFileType =    
    
        new FilePickerFileType(new Dictionary<DevicePlatform, IEnumerable<string>>  
        {  
            { DevicePlatform.Android, new[] { "application/foo", "application/octet-stream" } }  
        });  
    

    Note that because of the intent filter your application will be available when you open .foo files.

    0 comments No comments

  3. Sergey Svinolobov 0 Reputation points
    2023-07-02T23:29:27.7966667+00:00

    It's really annoying what I suppose to spend hours in tries to find a proper answer for so simple issue :(

    Microsoft, why you're making so buggy, so unpleasable code?! It's a pretty simple and common task - just pick your own file from the phone's publiс storage folder! Yes, my damn own .xml - not a "video", "image" or other crap you're "serving". Are you familiar with xml files, aren't you?! What is "uncommon" here?!

    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.