android Webview支持input type=file

http://david-wei.github.io/2015/07/07/Webview-%E6%94%AF%E6%8C%81-input-type-file/

在一个带有input tpye=file标签的Html页面,通过WebView,上传android手机上的图片,发现不工作。(在Ios和微信上完全正常工作)所以,需要研究一下Android的WebView,来支持type=file的标签。

WebView设置WebChromeClient

重写WebChromeClient中关于文件选择的方法,onShowFileChooser和openFileChooser。(项目中只需要选择图片,所以加上了图片过滤。)

public static final int INPUT_FILE_REQUEST_CODE = 1;
private ValueCallback<Uri> mUploadMessage;
private final static int FILECHOOSER_RESULTCODE = 2;
private ValueCallback<Uri[]> mFilePathCallback;

private String mCameraPhotoPath;
private WebChromeClient mWebChromeClient = new WebChromeClient() {

    // android 5.0
    public boolean onShowFileChooser(
            WebView webView, ValueCallback<Uri[]> filePathCallback,
            WebChromeClient.FileChooserParams fileChooserParams) {
        if (mFilePathCallback != null) {
            mFilePathCallback.onReceiveValue(null);
        }
        mFilePathCallback = filePathCallback;

        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
            // Create the File where the photo should go
            File photoFile = null;
            try {
                photoFile = createImageFile();
                takePictureIntent.putExtra("PhotoPath", mCameraPhotoPath);
            } catch (IOException ex) {
                // Error occurred while creating the File
                Log.e("WebViewSetting", "Unable to create Image File", ex);
            }

            // Continue only if the File was successfully created
            if (photoFile != null) {
                mCameraPhotoPath = "file:" + photoFile.getAbsolutePath();
                takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,
                        Uri.fromFile(photoFile));
            } else {
                takePictureIntent = null;
            }
        }

        Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
        contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
        contentSelectionIntent.setType("image/*");

        Intent[] intentArray;
        if (takePictureIntent != null) {
            intentArray = new Intent[]{takePictureIntent};
        } else {
            intentArray = new Intent[0];
        }

        Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
        chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
        chooserIntent.putExtra(Intent.EXTRA_TITLE, "Image Chooser");
        chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);

        startActivityForResult(chooserIntent, INPUT_FILE_REQUEST_CODE);

        return true;
    }

    //The undocumented magic method override
    //Eclipse will swear at you if you try to put @Override here
    // For Android 3.0+
    public void openFileChooser(ValueCallback<Uri> uploadMsg) {

        mUploadMessage = uploadMsg;
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        i.addCategory(Intent.CATEGORY_OPENABLE);
        i.setType("image/*");
        WebViewActivity.this.startActivityForResult(Intent.createChooser(i, "Image Chooser"), FILECHOOSER_RESULTCODE);

    }

    // For Android 3.0+
    public void openFileChooser(ValueCallback uploadMsg, String acceptType) {
        mUploadMessage = uploadMsg;
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        i.addCategory(Intent.CATEGORY_OPENABLE);
        i.setType("image/*");
        WebViewActivity.this.startActivityForResult(
                Intent.createChooser(i, "Image Chooser"),
                FILECHOOSER_RESULTCODE);
    }

    //For Android 4.1
    public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
        mUploadMessage = uploadMsg;
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        i.addCategory(Intent.CATEGORY_OPENABLE);
        i.setType("image/*");
        WebViewActivity.this.startActivityForResult(Intent.createChooser(i, "Image Chooser"), WebViewActivity.FILECHOOSER_RESULTCODE);

    }
};

选择结果的回调

在onActivityResult中获取对应的选取文件的返回结果

public void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == FILECHOOSER_RESULTCODE) {
        if (null == mUploadMessage) return;
        Uri result = data == null || resultCode != RESULT_OK ? null
                : data.getData();
        if (result != null) {
            String imagePath = ImageFilePath.getPath(this, result);
            if (!StrUtils.isEmpty(imagePath)) {
                result = Uri.parse("file:///" + imagePath);
            }
        }
        mUploadMessage.onReceiveValue(result);
        mUploadMessage = null;
    } else if (requestCode == INPUT_FILE_REQUEST_CODE && mFilePathCallback != null) {
        // 5.0的回调
        Uri[] results = null;

        // Check that the response is a good one
        if (resultCode == Activity.RESULT_OK) {
            if (data == null) {
                // If there is not data, then we may have taken a photo
                if (mCameraPhotoPath != null) {
                    Logger.d("camera_photo_path", mCameraPhotoPath);
                    results = new Uri[]{Uri.parse(mCameraPhotoPath)};
                }
            } else {
                String dataString = data.getDataString();
                Logger.d("camera_dataString", dataString);
                if (dataString != null) {
                    results = new Uri[]{Uri.parse(dataString)};
                }
            }
        }

        mFilePathCallback.onReceiveValue(results);
        mFilePathCallback = null;
    } else {
        super.onActivityResult(requestCode, resultCode, data);
        return;
    }
}

文件路径的获取

返回文件的解析,因为html页面需要的是文件,所以客户端需要返回的是对应文件的路径。这样,就会存在一个问题,在Android 4.4上,通过文件选择返回的结果都是对应以content开头格式的对应的路径。这就得需要咱们来进行判断,最终都需要转回成以file开头对应的格式文件。下面,我封装成了一个ImageFilePath的类,通过调用getPath方法来获取最终的结果。这个类的方法如下:

 /**
 * Method for return file path of Gallery image
 *
 * @param context
 * @param uri
 * @return path of the selected image file from gallery
 */
 public static String getPath(final Context context, final Uri uri) {

    // check here to KITKAT or new version
    final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;

    // DocumentProvider
    if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {

        // ExternalStorageProvider
        if (isExternalStorageDocument(uri)) {
            final String docId = DocumentsContract.getDocumentId(uri);
            final String[] split = docId.split(":");
            final String type = split[0];

            if ("primary".equalsIgnoreCase(type)) {
                return Environment.getExternalStorageDirectory() + "/"
                        + split[1];
            }
        }
        // DownloadsProvider
        else if (isDownloadsDocument(uri)) {

            final String id = DocumentsContract.getDocumentId(uri);
            final Uri contentUri = ContentUris.withAppendedId(
                    Uri.parse("content://downloads/public_downloads"),
                    Long.valueOf(id));

            return getDataColumn(context, contentUri, null, null);
        }
        // MediaProvider
        else if (isMediaDocument(uri)) {
            final String docId = DocumentsContract.getDocumentId(uri);
            final String[] split = docId.split(":");
            final String type = split[0];

            Uri contentUri = null;
            if ("image".equals(type)) {
                contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
            } else if ("video".equals(type)) {
                contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
            } else if ("audio".equals(type)) {
                contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
            }

            final String selection = "_id=?";
            final String[] selectionArgs = new String[] { split[1] };

            return getDataColumn(context, contentUri, selection,
                    selectionArgs);
        }
    }
    // MediaStore (and general)
    else if ("content".equalsIgnoreCase(uri.getScheme())) {

        // Return the remote address
        if (isGooglePhotosUri(uri))
            return uri.getLastPathSegment();

        return getDataColumn(context, uri, null, null);
    }
    // File
    else if ("file".equalsIgnoreCase(uri.getScheme())) {
        return uri.getPath();
    }

    return null;
}

/**
 * Get the value of the data column for this Uri. This is useful for
 * MediaStore Uris, and other file-based ContentProviders.
 *
 * @param context
 *            The context.
 * @param uri
 *            The Uri to query.
 * @param selection
 *            (Optional) Filter used in the query.
 * @param selectionArgs
 *            (Optional) Selection arguments used in the query.
 * @return The value of the _data column, which is typically a file path.
 */
public static String getDataColumn(Context context, Uri uri,
                                   String selection, String[] selectionArgs) {

    Cursor cursor = null;
    final String column = "_data";
    final String[] projection = { column };

    try {
        cursor = context.getContentResolver().query(uri, projection,
                selection, selectionArgs, null);
        if (cursor != null && cursor.moveToFirst()) {
            final int index = cursor.getColumnIndexOrThrow(column);
            return cursor.getString(index);
        }
    } finally {
        if (cursor != null)
            cursor.close();
    }
    return null;
}

/**
 * @param uri
 *            The Uri to check.
 * @return Whether the Uri authority is ExternalStorageProvider.
 */
public static boolean isExternalStorageDocument(Uri uri) {
    return "com.android.externalstorage.documents".equals(uri
            .getAuthority());
}

/**
 * @param uri
 *            The Uri to check.
 * @return Whether the Uri authority is DownloadsProvider.
 */
public static boolean isDownloadsDocument(Uri uri) {
    return "com.android.providers.downloads.documents".equals(uri
            .getAuthority());
}

/**
 * @param uri
 *            The Uri to check.
 * @return Whether the Uri authority is MediaProvider.
 */
public static boolean isMediaDocument(Uri uri) {
    return "com.android.providers.media.documents".equals(uri
            .getAuthority());
}

/**
 * @param uri
 *            The Uri to check.
 * @return Whether the Uri authority is Google Photos.
 */
public static boolean isGooglePhotosUri(Uri uri) {
    return "com.google.android.apps.photos.content".equals(uri
            .getAuthority());
}

参考资料

file-upload-in-webview
get-real-path-from-uri-android-kitkat-new-storage-access-framework

应网友要求添加缺失方法并附上全部源码:

package com.yunshouhu.android_webview_input_file;

import java.io.File;
import java.io.IOException;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;

public class WebViewActivity extends Activity{
	
	private static final String TAG = "MainAcivity";	
	private WebView webView;
	
	@SuppressLint("SetJavaScriptEnabled")
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		
		super.onCreate(savedInstanceState);
		
		setContentView(R.layout.main);
		webView=(WebView) findViewById(R.id.webView);
		webView.loadUrl("file:///android_asset/upload.html");
		WebSettings mWebSettings = webView.getSettings();
	    mWebSettings.setJavaScriptEnabled(true);
	    mWebSettings.setSupportZoom(false);
	    mWebSettings.setAllowFileAccess(true);
	    mWebSettings.setAllowFileAccess(true);
	    mWebSettings.setAllowContentAccess(true);
	    
		webView.setWebChromeClient(mWebChromeClient);
		webView.setWebViewClient(new WebViewClient(){
	           
			@Override
	        public boolean shouldOverrideUrlLoading(WebView view, String url) {
	         
	        	   //返回值是true的时候控制去WebView打开,为false调用系统浏览器或第三方浏览器
				Log.i(TAG, "shouldOverrideUrlLoading url="+url);
	            view.loadUrl(url);
	            return true;
	        }
	     });
		
		
		Log.i(TAG, "start");
		
	}
	
	public void go(View view)
	{
		Log.i(TAG, "go");
		webView.loadUrl("file:///android_asset/upload.html");
		webView.getSettings().setJavaScriptEnabled(true);
		
	}
	
	public static final int INPUT_FILE_REQUEST_CODE = 1;
	private ValueCallback<Uri> mUploadMessage;
	private final static int FILECHOOSER_RESULTCODE = 2;
	private ValueCallback<Uri[]> mFilePathCallback;

	private String mCameraPhotoPath;
	
	//在sdcard卡创建缩略图
	//createImageFileInSdcard
    @SuppressLint("SdCardPath")
	private File createImageFile() {
    	//mCameraPhotoPath="/mnt/sdcard/tmp.png";
    	File file=new File(Environment.getExternalStorageDirectory()+"/","tmp.png");
    	mCameraPhotoPath=file.getAbsolutePath();
    	if(!file.exists())
    	{
    		try {
				file.createNewFile();
			} catch (IOException e) {
				e.printStackTrace();
			}
    	}
    	return file;
	}
    
	private WebChromeClient mWebChromeClient = new WebChromeClient() {


	    // android 5.0 这里需要使用android5.0 sdk
	    public boolean onShowFileChooser(
	            WebView webView, ValueCallback<Uri[]> filePathCallback,
	            WebChromeClient.FileChooserParams fileChooserParams) {
	    	
	    	Log.d(TAG, "onShowFileChooser");
	        if (mFilePathCallback != null) {
	            mFilePathCallback.onReceiveValue(null);
	        }
	        
	        
	        mFilePathCallback = filePathCallback;

	        /**
  	Open Declaration   String android.provider.MediaStore.ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE"
	Standard Intent action that can be sent to have the camera application capture an image and return it. 
	The caller may pass an extra EXTRA_OUTPUT to control where this image will be written. If the EXTRA_OUTPUT is not present, then a small sized image is returned as a Bitmap object in the extra field. This is useful for applications that only need a small image. If the EXTRA_OUTPUT is present, then the full-sized image will be written to the Uri value of EXTRA_OUTPUT. As of android.os.Build.VERSION_CODES.LOLLIPOP, this uri can also be supplied through android.content.Intent.setClipData(ClipData). If using this approach, you still must supply the uri through the EXTRA_OUTPUT field for compatibility with old applications. If you don't set a ClipData, it will be copied there for you when calling Context.startActivity(Intent).
	See Also:EXTRA_OUTPUT
	标准意图,被发送到相机应用程序捕获一个图像,并返回它。通过一个额外的extra_output控制这个图像将被写入。如果extra_output是不存在的,
	那么一个小尺寸的图像作为位图对象返回。如果extra_output是存在的,那么全尺寸的图像将被写入extra_output URI值。
	         */
	        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
	        if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
	            // Create the File where the photo should go
	            File photoFile = null;
	            try {
	            	//设置MediaStore.EXTRA_OUTPUT路径,相机拍照写入的全路径
	                photoFile = createImageFile();
	                takePictureIntent.putExtra("PhotoPath", mCameraPhotoPath);
	            } catch (Exception ex) {
	                // Error occurred while creating the File
	                Log.e("WebViewSetting", "Unable to create Image File", ex);
	            }

	            // Continue only if the File was successfully created
	            if (photoFile != null) {
	                mCameraPhotoPath = "file:" + photoFile.getAbsolutePath();
	                takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,
	                        Uri.fromFile(photoFile));
	                System.out.println(mCameraPhotoPath);
	            } else {
	                takePictureIntent = null;
	            }
	        }

	        Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
	        contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
	        contentSelectionIntent.setType("image/*");
	        Intent[] intentArray;
	        if (takePictureIntent != null) {
	            intentArray = new Intent[]{takePictureIntent};
	            System.out.println(takePictureIntent);
	        } else {
	            intentArray = new Intent[0];
	        }

	        Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
	        chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
	        chooserIntent.putExtra(Intent.EXTRA_TITLE, "Image Chooser");
	        chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);

	        startActivityForResult(chooserIntent, INPUT_FILE_REQUEST_CODE);

	        return true;
	    }



		//The undocumented magic method override
	    //Eclipse will swear at you if you try to put @Override here
	    // For Android 3.0+
	    public void openFileChooser(ValueCallback<Uri> uploadMsg) {
	    	Log.d(TAG, "openFileChooser1");
	    	
	        mUploadMessage = uploadMsg;
	        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
	        i.addCategory(Intent.CATEGORY_OPENABLE);
	        i.setType("image/*");
	        WebViewActivity.this.startActivityForResult(Intent.createChooser(i, "Image Chooser"), FILECHOOSER_RESULTCODE);

	    }

	    // For Android 3.0+
	    public void openFileChooser(ValueCallback uploadMsg, String acceptType) {
	    	Log.d(TAG, "openFileChooser2");
	        mUploadMessage = uploadMsg;
	        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
	        i.addCategory(Intent.CATEGORY_OPENABLE);
	        i.setType("image/*");
	        WebViewActivity.this.startActivityForResult(
	                Intent.createChooser(i, "Image Chooser"),
	                FILECHOOSER_RESULTCODE);
	    }

	    //For Android 4.1
	    public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
	    	Log.d(TAG, "openFileChooser3");
	    	
	        mUploadMessage = uploadMsg;
	        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
	        i.addCategory(Intent.CATEGORY_OPENABLE);
	        i.setType("image/*");
	        WebViewActivity.this.startActivityForResult(Intent.createChooser(i, "Image Chooser"), WebViewActivity.FILECHOOSER_RESULTCODE);

	    }
	};
	
	public void onActivityResult(int requestCode, int resultCode, Intent data) {
		Log.d(TAG, "onActivityResult");
		
	    if (requestCode == FILECHOOSER_RESULTCODE) {
	        if (null == mUploadMessage) return;
	        Uri result = data == null || resultCode != RESULT_OK ? null
	                : data.getData();
	        if (result != null) {
	            String imagePath = ImageFilePath.getPath(this, result);
	            if (!TextUtils.isEmpty(imagePath)) {
	                result = Uri.parse("file:///" + imagePath);
	            }
	        }
	        mUploadMessage.onReceiveValue(result);
	        mUploadMessage = null;
	    } else if (requestCode == INPUT_FILE_REQUEST_CODE && mFilePathCallback != null) {
	        // 5.0的回调
	        Uri[] results = null;

	        // Check that the response is a good one
	        if (resultCode == Activity.RESULT_OK) {
	            if (data == null) {
	                // If there is not data, then we may have taken a photo
	                if (mCameraPhotoPath != null) {
	                    Logger.d("camera_photo_path", mCameraPhotoPath);
	                    results = new Uri[]{Uri.parse(mCameraPhotoPath)};
	                }
	            } else {
	                String dataString = data.getDataString();
	                Logger.d("camera_dataString", dataString);
	                if (dataString != null) {
	                    results = new Uri[]{Uri.parse(dataString)};
	                }
	            }
	        }

	        mFilePathCallback.onReceiveValue(results);
	        mFilePathCallback = null;
	    } else {
	        super.onActivityResult(requestCode, resultCode, data);
	        return;
	    }
	}
	
}
源码下载,另存为zip后,需要使用winrar解压:



  • 9
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 25
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 25
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值