.Net MAUI Webview file upload not working

Arif uzzaman 15 Reputation points
2023-08-10T06:58:07.5233333+00:00

I'm very new in .Net MAUI. Working on a webview that have file upload option. I added read/write permissions to AndroidManifest.xml. Permission popup shows and it granted. But, no file uploader shows up.

I found some xamarin code via google search, but can't able to implement. Can anyone please give a .Net MAUI working code for webview file uploder.

Thanks in advance.

Developer technologies .NET .NET MAUI
{count} votes

2 answers

Sort by: Most helpful
  1. Arif uzzaman 15 Reputation points
    2023-08-21T03:24:12.4766667+00:00

    After lots of back and forth trying, able to solve the issue. Below is my complete solution. Hope it will help other who needs it in future.

    Thank you so much Yonglun Liu for your big help.

    Step 1. Modify the MainActivity under the Platfrom/Android folder.

    public class MainActivity : MauiAppCompatActivity
    {
    	public static IValueCallback mUploadCallbackAboveL;     
    	public static Android.Net.Uri imageUri;     
    	public static MainActivity Instance;     
    	public static int PHOTO_REQUEST = 10023;
    
    	 protected override void OnCreate(Bundle bundle)     {                   
    		base.OnCreate(bundle);        
    		Instance = this;          
    
    		if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.Camera) != (int)Permission.Granted){          	  
    
    		}     
    	}
    
    	protected override void OnActivityResult(int requestCode, Result resultCode, Android.Content.Intent intent)	
    	{
    		if (resultCode == Result.Ok){             
    			if (intent != null && intent.Data !=null) // File picker selected photo   
    			{                 
    				if (null == mUploadCallbackAboveL) return;                
    				Android.Net.Uri result = intent == null || resultCode != Result.Ok ? null : intent.Data;                 	
    				//mUploadMessage.OnReceiveValue(result);                 	
    				mUploadCallbackAboveL.OnReceiveValue(new Android.Net.Uri[] { result });                 	
    				mUploadCallbackAboveL = null;             
    			}             
    			else if (requestCode == PHOTO_REQUEST){                 
    				if (mUploadCallbackAboveL != null){                     	
    					onActivityResultAboveL(requestCode, resultCode, intent);                 
    				}                             	
    			}         
    		}         
    		else {             
    			if (resultCode != Result.Ok){                 
    				mUploadCallbackAboveL.OnReceiveValue(null);                 
    				mUploadCallbackAboveL = null;                 
    				return;             
    			}         
    		}
    	}
    
    	private void onActivityResultAboveL(int requestCode, Result resultCode, Android.Content.Intent data){
    	
    		if (requestCode != PHOTO_REQUEST || mUploadCallbackAboveL == null){
    			return;
    		}
    
    		Android.Net.Uri[] results = null;         
    		if (resultCode == Result.Ok){             
    			results = new Android.Net.Uri[] { imageUri };             
    			results[0] = MainActivity.imageUri;         
    		}         
    
    		mUploadCallbackAboveL.OnReceiveValue(results);         
    		mUploadCallbackAboveL = null;
    	}
    
    }
    

    Step 2. Implement a custom WebChromeClient under the Platfrom/Android folder.

    internal class MyWebChromeClient : WebChromeClient{
    
    	private MainActivity activity = null;
    
    	public MyWebChromeClient(MainActivity context){             
    		this.activity = context;         
    	}
    
    	public override void OnPermissionRequest(PermissionRequest request){
    		foreach (var resource in request.GetResources()) {                 
    			if (resource.Equals(PermissionRequest.ResourceVideoCapture, StringComparison.OrdinalIgnoreCase))                 		
    			{	
    				// Get the status of the .NET MAUI app's access to the camera                     
    				PermissionStatus status = Permissions.CheckStatusAsync<Permissions.Camera>().Result;                      			
    				
    				// Deny the web page's request if the app's access to the camera is not "Granted"                     
    				if (status != PermissionStatus.Granted)                         
    					request.Deny();                     
    				else                         
    					request.Grant(request.GetResources());
                          
    				return;                 
    			}             
    		}            
                 
    		base.OnPermissionRequest(request);
    	}
    
    	public override bool OnShowFileChooser(WebView webView, IValueCallback filePathCallback, FileChooserParams fileChooserParams){
    		MainActivity.mUploadCallbackAboveL = filePathCallback;
    
    		//PickPhoto;              
    		var imageStorageDir = FileSystem.Current.CacheDirectory;
    
    		// Create picked image file path and name, add ticks to make it unique               
    		var file = new Java.IO.File(imageStorageDir + Java.IO.File.Separator + "IMG_" + DateTime.Now.Ticks + ".jpg");
    
    		//MainActivity.imageUri = Uri.FromFile(file);             
    		MainActivity.imageUri = FileProvider.GetUriForFile(activity, activity.PackageName + ".fileprovider", file);
    
    		//Create camera capture image intent and add it to the chooser               
    		var captureIntent = new Intent(MediaStore.ActionImageCapture);             			
    		captureIntent.PutExtra(MediaStore.ExtraOutput, MainActivity.imageUri);
    
    		var i = new Intent(Intent.ActionGetContent);             
    		i.AddCategory(Intent.CategoryOpenable);             
    		i.SetType("image/*");
    
    		var chooserIntent = Intent.CreateChooser(i, "Choose Image");             		
    		chooserIntent.PutExtra(Intent.ExtraInitialIntents, new Intent[] { captureIntent });
    
    		MainActivity.Instance.StartActivityForResult(chooserIntent, MainActivity.PHOTO_REQUEST);
    
    		return true;
    	}
    }
    

    Step 3. Modify the AndroidManifest.xml under the Platfrom/Android folder.

    <application android:allowBackup="true" android:icon="@mipmap/appicon" android:supportsRtl="true">
         <provider  android:name="androidx.core.content.FileProvider"         
    		android:authorities="YOUR_PACKAGE_NAME.fileprovider" android:exported="false"         
    		android:grantUriPermissions="true">       
    		<meta-data  android:name="android.support.FILE_PROVIDER_PATHS"           
    			android:resource="@xml/file_paths"></meta-data>     
    	 </provider>    
    </application>
    
    <uses-permission android:name="android.permission.CAMERA" />   
    <!--<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />   <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" />-->   <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>   
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />   
       
    <!-- Required only if your app needs to access images or photos that other apps created -->   
    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />   
    
    <!-- Required only if your app needs to access videos that other apps created -->   
    <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />   
    
    <!-- Required only if your app needs to access audio files that other apps created -->   
    <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />    	
    <uses-feature android:name="android.hardware.camera"/> 	
    
    <uses-sdk android:minSdkVersion="23" android:targetSdkVersion="33" />    
    <queries>     
    	<intent>       
    		<action android:name="android.media.action.IMAGE_CAPTURE" />     
    	</intent>  
    </queries>
    
    

    Step 4. MainPage.Xaml looks like below.

    <?xml version="1.0" encoding="utf-8" ?> 
    <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"              	
    	xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"              
    	x:Class="UPloadFile.MainPage"              
    	Title="Uplodad file webview">              
    
    	<WebView Loaded="WebView_Loaded"              
    		x:Name="myWebView" 
    		Source="https://xyz.com">         
    	</WebView>  
    </ContentPage>
    
    
    

    Step 5. MainPage.Xaml.cs looks like below.

    public partial class MainPage : ContentPage
    {
    	public Dashboard(){
    		InitializeComponent();
    	}
    
    	protected override void OnHandlerChanged(){
    		base.OnHandlerChanged();
    
    		#if ANDROID
    			var webview = myWebView.Handler.PlatformView as Android.Webkit.WebView;         
    			webview.SetWebChromeClient(new STC_Delivery_App.Platforms.Android.MyWebChromeClient(MainActivity.Instance));          
    			if (webview is not null){             
    				webview.Settings.MediaPlaybackRequiresUserGesture = false;             	
    				webview.Settings.SetGeolocationEnabled(true);             
    				webview.Settings.JavaScriptEnabled = true;         
    			}
    		#endif
    	}
    
    	private async void WebView_Loaded(object sender, EventArgs e){
    		
    		PermissionStatus status = await Permissions.CheckStatusAsync<Permissions.Camera>();
    
    		if (status != PermissionStatus.Granted){             
    			if (Permissions.ShouldShowRationale<Permissions.Camera>()) { }              
    			await Permissions.RequestAsync<Permissions.Camera>();         
    		}
    
    		status = await Permissions.CheckStatusAsync<Permissions.StorageRead>();
    		
    		if (status != PermissionStatus.Granted){             
    			if (Permissions.ShouldShowRationale<Permissions.StorageRead>()) { }              
    			await Permissions.RequestAsync<Permissions.StorageRead>();         
    		}
    
    		status = await Permissions.CheckStatusAsync<Permissions.StorageWrite>();
    
    		if (status != PermissionStatus.Granted){             
    			if (Permissions.ShouldShowRationale<Permissions.StorageWrite>()) { }              
    			await Permissions.RequestAsync<Permissions.StorageWrite>();         
    		}
    	}
    }
    
    2 people found this answer helpful.

  2. Yonglun Liu (Shanghai Wicresoft Co,.Ltd.) 50,126 Reputation points Microsoft External Staff
    2023-08-11T05:26:40.0066667+00:00

    Hello,

    After investigation and testing, you could refer to the following steps in MAUI to set up ChromeClient for webview.

    Step 1. Modify the MainActivity under the Platfrom/Android folder.

    public class MainActivity : MauiAppCompatActivity
    {
        public static IValueCallback mUploadCallbackAboveL;
        public static Android.Net.Uri imageUri;
        public static MainActivity Instance;
        public static int PHOTO_REQUEST = 10023;
        public static IValueCallback mUploadMessage;
        public static int FILECHOOSER_RESULTCODE = 1;
    
        protected override void OnCreate(Bundle bundle)
        {
            base.OnCreate(bundle);
    
            Instance = this;
    
            if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.Camera) != (int)Permission.Granted)
            {
               
            }
        }
    
        protected override void OnActivityResult(int requestCode, Result resultCode, Intent intent)
        {
            if (requestCode == FILECHOOSER_RESULTCODE)
            {
                if (null == mUploadMessage) return;
                Android.Net.Uri result = intent == null || resultCode != Result.Ok ? null : intent.Data;
                mUploadMessage.OnReceiveValue(result);
                mUploadMessage = null;
            }
            else if (requestCode == PHOTO_REQUEST)
            {
                Android.Net.Uri result = intent == null || resultCode != Result.Ok ? null : intent.Data;
    
                if (mUploadCallbackAboveL != null)
                {
                    onActivityResultAboveL(requestCode, resultCode, intent);
                }
                else if (mUploadMessage != null)
                {
                    mUploadMessage.OnReceiveValue(result);
                    mUploadMessage = null;
                }
            }
        }
        private void onActivityResultAboveL(int requestCode, Result resultCode, Intent data)
        {
            if (requestCode != PHOTO_REQUEST || mUploadCallbackAboveL == null)
            {
                return;
            }
            Android.Net.Uri[] results = null;
            if (resultCode == Result.Ok)
            {
               
                results = new Android.Net.Uri[] { imageUri };
                results[0] = MainActivity.imageUri;
    
                //  The data.Data property contains the file Uri of your choice, which you can manipulate through this Uri
            }
            mUploadCallbackAboveL.OnReceiveValue(results);
            mUploadCallbackAboveL = null;
        }
    }
    
    

    Step 2. Implement a custom WebChromeClient under the Platfrom/Android folder.

    public class CustomWebChromeClient : WebChromeClient
        {
            Activity mActivity = null;
            public CustomWebChromeClient(Activity activity)
            {
                mActivity = activity;
            }
            public override bool OnShowFileChooser(WebView webView, IValueCallback filePathCallback, FileChooserParams fileChooserParams)
            {
                MainActivity.mUploadCallbackAboveL = filePathCallback;
    
                //PickPhoto; 
                var imageStorageDir = FileSystem.Current.CacheDirectory;
               
                // Create picked image file path and name, add ticks to make it unique  
                var file = new Java.IO.File(imageStorageDir + Java.IO.File.Separator + "IMG_" + DateTime.Now.Ticks + ".jpg");
    
                MainActivity.imageUri = Uri.FromFile(file);
               
                //// Create image picker intent and add it to the chooser        
                var captureIntent = new Intent(MediaStore.ActionPickImages);
                captureIntent.PutExtra(MediaStore.ExtraOutput, MainActivity.imageUri);
    
                var i = new Intent(Intent.ActionGetContent);
                i.AddCategory(Intent.CategoryOpenable);
                i.SetType("image/*");
    
                var chooserIntent = Intent.CreateChooser(i, "Choose image");
                chooserIntent.PutExtra(Intent.ExtraInitialIntents, new Intent[] { captureIntent });
    
                MainActivity.Instance.StartActivityForResult(chooserIntent, MainActivity.PHOTO_REQUEST);
                return true;
            }
        }
    
    

    Step 3. In MAUI, suppose there is a WebView page that picks images as follows:

                    <WebView x:Name="wv" HeightRequest="1000" WidthRequest="400">
                        <WebView.Source>
                            <HtmlWebViewSource>
                                <HtmlWebViewSource.Html>
                                    <![CDATA[
                    <html>
                    <head>
                    </head>
                    <body>
                    <input type='file' name='images[]' multiple accept="image/*" capture='camera' />
                    </body>
                    </html>                    
                    ]]>
                                </HtmlWebViewSource.Html>
                            </HtmlWebViewSource>
                        </WebView.Source>
                    </WebView>
    

    Step 4. Set up a custom ChromeClient for webview in the code-behind:

        protected override void OnHandlerChanged()
        {
            base.OnHandlerChanged();
            var web = wv.Handler.PlatformView as Android.Webkit.WebView;
            web.SetWebChromeClient(new CustomWebChromeClient(MainActivity.Instance));
                
        }
    

    Please refer to the following documentations:

    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.

    1 person found this answer helpful.

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.