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 mUploadMessage;private final static int FILECHOOSER_RESULTCODE = 2;private ValueCallback mFilePathCallback;private String mCameraPhotoPath;private WebChromeClient mWebChromeClient = new WebChromeClient() {    // android 5.0    public boolean onShowFileChooser(            WebView webView, ValueCallback 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 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 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")@Overrideprotected 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 mUploadMessage;private final static int FILECHOOSER_RESULTCODE = 2;private ValueCallback 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 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 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 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解压:

android Webview支持input type=file_第1张图片


更多相关文章

  1. android的文件操作 sdcard和rom
  2. android studio中断开SVN连接,并彻底清理项目中的.svn文件
  3. 第二十篇 Android获取本机图片、音频、视频、文档以及本地文件夹
  4. Android中资源文件用法简单示例
  5. Unity3d导出Android的apk文件时相关问题的解决办法
  6. android编译系统的makefile文件Android.mk写法
  7. android文件系统挂载分析
  8. Android 自定义文件路径选择器

随机推荐

  1. Android录音上————AudioRecord实现录
  2. android之Handler整理
  3. android客户端与服务端交互的工具类
  4. android的短信发送全过程源代码分析
  5. 打开Android(安卓)Studio报错 "required
  6. Android(安卓)APK签名对比及说明
  7. Android(安卓)使用 URL 和 AsyncTask 加
  8. Android中Service(服务)和Thread(线程)的关
  9. Android(安卓)判断SD卡是否存在及容量查
  10. javascript检查android软键盘隐藏显示