Android WebView H5 input file 不支持 multiple 属性多选文件的解决方案

Andrid WebView 不支持文件多选

之前使用 Vue 开发了一个 H5 移动端项目,其中有图片上传功能,可拍照上传也可选择文件上传,现在想增加批量多选文件上传,然后发现 Android 竟然不支持 inputmultiple 属性。

网上一翻搜索得出以下结论:如果只是开发 H5 页面,那么很遗憾无法解决。

参考:https://segmentfault.com/q/1010000002739580

20191110163633.png

Android web对于input-file的支持不太好, 到了android 4.4是连选择文件的窗口都打不开....
这个如果你是app里面嵌入webview,建议上传文件这一环节交给原生去做,原生上传好文件给到web文件信息.
如果是纯webapp的话,微信可以用公众平台提供的js api,里面有上传图片的接口.其它的web就没什么好的方式了.

解决方案

如果是自己开发了 Android 原生应用,那可以通过自定义 WebView 来实现,还好我的 H5 页面是嵌入在原生应用中运行的。

核心代码如下:

//上传文件相关变量
private Uri mImageUri;
private ValueCallback<Uri> mUploadMessage;
private ValueCallback<Uri[]> mUploadCallbackAboveL; //Android 5.0+
private static final int REQUEST_CODE_SELECT_FILE = 2;

/**
 * 文件选择返回结果,Android L 以上用
 *
 * @param requestCode
 * @param resultCode
 * @param intent
 */
@SuppressWarnings("null")
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void onActivityResultAboveL(int requestCode, int resultCode, Intent intent) {
    if (requestCode != REQUEST_CODE_SELECT_FILE || mUploadCallbackAboveL == null) {
        return;
    }

    Uri[] results = null;
    if (resultCode == Activity.RESULT_OK) {
        if (intent != null) {
            String dataString = intent.getDataString(); //单选
            ClipData clipData = intent.getClipData(); //多选

            if (clipData != null) {
                results = new Uri[clipData.getItemCount()];
                for (int i = 0; i < clipData.getItemCount(); i++) {
                    results[i] = clipData.getItemAt(i).getUri();
                }
            } else if (dataString != null) {
                results = new Uri[]{Uri.parse(dataString)};
            }
        }

        if (results == null) {
            results = new Uri[]{mImageUri};
        }
    }

    mUploadCallbackAboveL.onReceiveValue(results);
    mUploadCallbackAboveL = null;
}


/**
 * 初始化webView
 */
private void initWebView() {
    mWebView = findViewById(R.id.webView);
    mWebView.setDefaultHandler(new DefaultHandler());
    mWebView.getSettings().setJavaScriptEnabled(true);
    mWebView.getSettings().setDomStorageEnabled(true);
    if (Build.VERSION.SDK_INT >= 21) {
        //android 5.0 以上允许同时加载 http 和 https
        mWebView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
    }

    //TODO:临时不用缓存,防止服务端更新后客户端仍加载旧页面
    mWebView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
    deleteDatabase("WebView.db");
    deleteDatabase("WebViewCache.db");
    mWebView.clearCache(true);
    mWebView.clearFormData();
    getCacheDir().delete(); //清空缓存目录;

    mWebView.setWebChromeClient(new WebChromeClient() {

        /** 下面的几个方法用于解决web中type="file"的按钮不能选择文件的问题 **/

        //Android 3.0以下
        protected void openFileChooser(ValueCallback<Uri> uploadMsg) {
            mUploadMessage = uploadMsg;
            takeImg();
        }

        //Android 3.0+,android 4.0 以下
        protected void openFileChooser(ValueCallback uploadMsg, String acceptType) {
            mUploadMessage = uploadMsg;
            takeImg();
        }

        //Android 4.0-4.3
        protected void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
            mUploadMessage = uploadMsg;
            takeImg();
        }

        //Android 4.4 无方法,囧~~

        //Android 5.0+
        @TargetApi(Build.VERSION_CODES.LOLLIPOP)
        public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
            if (mUploadCallbackAboveL != null) {
                mUploadCallbackAboveL.onReceiveValue(null);
                mUploadCallbackAboveL = null;
            }

            mUploadCallbackAboveL = filePathCallback;

            takeImg();

            return true;
        }
    });
    mWebView.loadUrl(mUrl);

    //注册供js调用的方法
    registerJsMethod();

    //发送消息给js
    mWebView.send("hello");
}


/**
 * 拍照或选择照片(android L 及以上支持多选)
 */
private void takeImg() {
    //创建图片存储目录及生成文件名,如:/storage/emulated/0/Pictures/Temp/IMG_20180809124757356.jpg
    File imageStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "Temp");
    if (!imageStorageDir.exists()) {
        imageStorageDir.mkdirs();
    }
    SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddhhmmssSSS");
    String dateString = sdf.format(new Date());
    File file = new File(imageStorageDir + File.separator + "IMG_" + dateString + ".jpg");
    mImageUri = Uri.fromFile(file);

    //遍历所有相机应用
    final List<Intent> cameraIntents = new ArrayList<>();
    final Intent captureIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
    final PackageManager packageManager = getPackageManager();
    final List<ResolveInfo> listCam = packageManager.queryIntentActivities(captureIntent, 0);
    for (ResolveInfo res : listCam) {
        final String packageName = res.activityInfo.packageName;
        final Intent i = new Intent(captureIntent);
        i.setComponent(new ComponentName(res.activityInfo.packageName, res.activityInfo.name));
        i.setPackage(packageName);
        i.putExtra(MediaStore.EXTRA_OUTPUT, mImageUri);
        i.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); //多选
        cameraIntents.add(i);
    }

    //浏览图片Intent+所有相机应用Intent
    Intent i = new Intent(Intent.ACTION_GET_CONTENT);
    i.addCategory(Intent.CATEGORY_OPENABLE);
    i.setType("image/*");
    i.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); //多选
    Intent chooserIntent = Intent.createChooser(i, "选择图片或相机拍摄");
    chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, cameraIntents.toArray(new Parcelable[]{}));

    //打开浏览或相机
    startActivityForResult(chooserIntent, REQUEST_CODE_SELECT_FILE);
}

效果演示

20191110164803.png

20191110164750.png

最后修改:2019 年 11 月 10 日 04 : 48 PM
如果觉得我的文章对你有用,请随意赞赏

发表评论