您的位置:首页 > 移动开发

Android通过WebView选择文件上传(支持直接调起相机等应用)

2016-10-02 21:02 656 查看
前言

最近项目中上的一个banner,里面有个需求,需要在网页中调用系统的拍照功能或者选择文件,获取图片在网页显示出来,并上传到客户的服务器。但是在banner上线后,发现我们的app支持的Webview中不支持这个功能,而把链接复制到系统自带的或者一些第三方浏览器中都可以进行拍照/文件管理器选择,所以我知道这肯定是通用的js,而我们app做的支持不够了。接着,我决定找找原因。

一番查找之后,我发现这个功能,简单而言,其实就是在一个html页面中有这样一段类似的代码

<input class="filePrew" type="file" capture="camera" accept="image/*" name="image">


,我们要在 webview 中添加对这一类 js 的支持。

实现

通过我大Stack Overflow的反馈,我采用了实现这种功能的目前的一种通用做法:

第一步:自定义webview的WebChromeClient,在其中覆盖掉openFileChooser方法。

注意:这里是WebChromeClient,不是WebViewClient。而且openFileChooser方法在WebChromeClient中有@hide标记,不过只管重写即可。


贴上代码:

/** 选择文件进行上传 */
// Andorid 4.1+
public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType, String capture) {
openFileChooser(uploadFile, acceptType);
}

// Andorid 3.0 +
public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType) {
mUploadFile = uploadFile;
act.startActivityForResult(createDefaultOpenableIntent(), REQUEST_UPLOAD_FILE_CODE);
}

// Android 3.0
public void openFileChooser(ValueCallback<Uri> uploadFile) {
openFileChooser(uploadFile, "");
}


上面的createDefaultOpenableIntent()方法,我借鉴了源码com.android.browser.UploadHandler,下面也把相关代码给出:

/** 默认支持上传多种文件类型 */
private Intent createDefaultOpenableIntent() {
// Create and return a chooser with the default OPENABLE
// actions including the camera, camcorder and sound
// recorder where available.
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("*/*");

Intent camcorderIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
Intent soundRecorderIntent = new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION);

Intent chooser = createChooserIntent(createCameraIntent(), soundRecorderIntent);
chooser.putExtra(Intent.EXTRA_INTENT, i);
chooser.putExtra(Intent.EXTRA_TITLE, "请选择要上传的文件");
return chooser;
}

private Intent createCameraIntent() {
Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File externalDataDir = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DCIM);
File cameraDataDir = new File(externalDataDir.getAbsolutePath() +
File.separator + "browser-photos");
cameraDataDir.mkdirs();
mCameraFilePath = cameraDataDir.getAbsolutePath() + File.separator +
System.currentTimeMillis() + ".jpg";
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(mCameraFilePath)));
return cameraIntent;
}

private Intent createChooserIntent(Intent... intents) {
Intent chooser = new Intent(Intent.ACTION_CHOOSER);
chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, intents);
return chooser;
}


注意:在创建 chooser 时,想要同时令多种类型的 intent 生效,要先把这些 intent 存放在Intents[]中,再调用putExtra(Intent.EXTRA_INITIAL_INTENTS, intents)方法把它们添加到 chooser 中;而不是调用“方法一个一个地添加,这样得到的 chooser 里面会只有一个额外 intent 生效。

第二步:在Activity的onActivityResult方法中进行处理,获取对应文件。

代码继续奉上:

if (requestCode == REQUEST_UPLOAD_FILE_CODE) {
if (resultCode == RESULT_OK) {
// 获取需上传文件成功
if (null == mUploadFile) {
return;
}
Uri result = (null == data) ? null : data.getData();
if (result == null) {
// 从相机获取
File cameraFile = new File(mCameraFilePath);
if (cameraFile.exists()) {
result = Uri.fromFile(cameraFile);
// 扫描相册
act.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, result));
}
}
try {
if (mUploadFile != null) {
mUploadFile.onReceiveValue(result);
mUploadFile = null;
}
} catch (Exception e) {
MeilaLog.e(TAG, e);
}
} else {
// 获取需上传文件失败或者取消操作
if (mUploadFile != null) {
mUploadFile.onReceiveValue(null);
mUploadFile = null;
}
}
}


这里也有一点需要注意的是,不管请求是否成功,在这个回调里面我们都要执行mUploadFile.onReceiveValue(null);,否则,h5 中的那个 js 在执行一次后,将不再起作用。

最后,上面代码用到的相关变量:

private ValueCallback<Uri> mUploadFile;
private String mCameraFilePath;
private static final int REQUEST_UPLOAD_FILE_CODE = 1003; // 选择文件请求码


总结

这样就可以实现在WebView中上传文件了,当然还要记得要给Activity添加相应的权限~

经手头上的机子测试验证,上面的代码处理,已经可以支持以下这几种机型(系统):小米、华为、魅族、vivo、oppo……排名不分先后,哈哈~

已知问题:在锤子系统上,会出现选择完图片返回 webview 无法正常读取图片的问题(图片位置显示空白),但是通过调起相机拍摄的照片是可以正常读取到(当然了,相机我做了单独处理啊,妈蛋>_<)。

由于项目中是对全文件类型支持,所以不好单独对返回的uri进行相关图片处理,后续会加上js发起的文件类型的判断,也就是前面openFileChooser方法中的acceptType参数,到时候再尝试对这个地方针对image类型做处理。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: