1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > H5调用本地相册/相机上传图片

H5调用本地相册/相机上传图片

时间:2019-01-24 21:51:19

相关推荐

H5调用本地相册/相机上传图片

在开发中有时候会用到H5调用本地图片或者相机,像第三方的实名认证,在线客服等等都需要上传图片。H5中只需要通过<input>调用即可,ios是可以的实现的,不需要自己处理,除非客户端压缩图片,但是Android中需要自己处理,比较坑,本文记录H5调用本地图片或者相机的实现过程以及遇到的问题。

先看效果图

H5

H5主要是通过input标签来获取图片

<input class="upload-input" accept="image/*" multiple required type="file" @change="chooseImg">

Android

通过WebView加载h5页面,监听WebView对应的方法,实现自己的逻辑。

WebView初始化,放开对应的权限

private fun initWebView() {val settings = mWebView.settingssettings.javaScriptEnabled = truesettings.javaScriptCanOpenWindowsAutomatically = true//自适应屏幕settings.layoutAlgorithm = SINGLE_COLUMNsettings.loadWithOverviewMode = truesettings.domStorageEnabled = truesettings.databaseEnabled = truesettings.setSupportZoom(false)mWebView.isHorizontalScrollBarEnabled = falsemWebView.isVerticalScrollBarEnabled = falsemWebView.webViewClient = webViewClientmWebView.webChromeClient = webViewChromeClient}

设置WebChromeClient

当点击input标签的时候会调用WebChromeClientonShowFileChooser()方法(5.0+)或者openFileChooser()方法(3.0+)在改方法中处理自己逻辑,这里是弹出一个原生的选择框,选择从相册还是拍照获取图片。

private val webViewChromeClient = object : WebChromeClient() {override fun onShowFileChooser(webView: WebView, filePathCallback: ValueCallback<Array<Uri>>,fileChooserParams: FileChooserParams): Boolean {mFilePathCallback = filePathCallbackval acceptTypes = fileChooserParams.acceptTypes//判断文件类型if (acceptTypes.contains("image/*")) {showSelectDialog()}return true}}

ValueCallbacksWebView组件提供给我们的,它里面包含了一个或者一组Uri,然后我们在onActivityResult()里将Uri传给ValueCallbacksonReceiveValue()方法,这样就把文件回传给H5端了。

注意:这里会遇到onShowFileChooser()只调用一次的问题,也就是第二次点击按钮的时候该方法不再调用,因为每次调用该方法都需要设置一个回调,不管是不是选择了照片,没有就回传一个null

/*** 显示相册/拍照选择对话框*/private fun showSelectDialog() {if (mSelectPhotoDialog == null) {mSelectPhotoDialog = SelectDialog(this, View.OnClickListener { view ->when (view.id) {R.id.tv_camera -> startCamera()R.id.tv_photo -> startAlbum()//不管选择还是不选择,必须有返回结果,否则就会调用一次R.id.tv_cancel -> {mFilePathCallback?.onReceiveValue(null)mFilePathCallback = null}}})}mSelectPhotoDialog?.show()}

拍照和打开相册功能封装到了PhotoUtils工具类中了。

/*** 拍照* @param context Activity*/fun startCamera(context: Activity) {val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)PATH_PHOTO = getSdCardDirectory(context) + "/temp.png"val temp = File(PATH_PHOTO)if (!temp.parentFile.exists()) {temp.parentFile.mkdirs()}if (temp.exists()) {temp.delete()}if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {//添加这一句表示对目标应用临时授权该Uri所代表的文件intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)// 通过FileProvider创建一个content类型的Urival uri: Uri = FileProvider.getUriForFile(context, context.packageName + ".fileprovider", temp)intent.putExtra(MediaStore.EXTRA_OUTPUT, uri)} else {intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(temp))}context.startActivityForResult(intent, RESULT_CODE_CAMERA)}

注意:拍照需要适配7.0,否者拿不到文件,Android 7.0 以后 通过FileProvider在应用间共享文件;此外拍完照片获取文件的时候不要通过intent来获取数据,直接通过这里指定的存储路径来获取,intent获取的一直是null

<application......<!-- Android7.0拍照权限适配 --><providerandroid:name="androidx.core.content.FileProvider"android:authorities="com.gfd.h5pic.fileprovider"android:exported="false"android:grantUriPermissions="true"><meta-dataandroid:name="android.support.FILE_PROVIDER_PATHS"android:resource="@xml/file_paths" /></provider></application>

file_paths.xml

<?xml version="1.0" encoding="utf-8"?><resources><paths><cache-pathname="cache"path="" /><external-pathname="sdcard_files"path="" /><external-pathname="external"path="" /><external-files-pathname="external_files"path="" /><external-cache-pathname="external_cache"path="" /><root-pathname="ex_sdcard_files"path="" /><!--相机相册裁剪--><external-files-pathname="camera_has_sdcard"path="file/" /><files-pathname="camera_no_sdcard"path="" /></paths></resources>

从相册中选择

/*** 打开相册* @param context Activity*/fun startAlbum(context: Activity) {val albumIntent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)albumIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);albumIntent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*")context.startActivityForResult(albumIntent, RESULT_CODE_PHOTO)}

onActivityResult()方法中通过intent获取选取的照片。

onActivityResult()方法处理

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {super.onActivityResult(requestCode, resultCode, data)if (requestCode == PhotoUtils.RESULT_CODE_CAMERA && resultCode == Activity.RESULT_OK) {//拍照并确定compressPicture(PhotoUtils.PATH_PHOTO)} else if (requestCode == PhotoUtils.RESULT_CODE_PHOTO && resultCode == Activity.RESULT_OK) {//相册选择并确定val result = data?.dataval path = result?.let { PhotoUtils.getPath(this, it) }if (path == null) {mFilePathCallback?.onReceiveValue(null)} else {compressPicture(path)}} else {mFilePathCallback?.onReceiveValue(null)}}

这里需要注意的是要判断resultCode,不然拍照完成,选择的不是确定而是取消,也是会上传图片的。

这里使用Luban压缩以后再上传的

/*** 压缩图片*/private fun compressPicture(path: String) {pressPicture(this, path, object : PhotoUtils.OnPictureCompressListener() {override fun onSuccess(file: File) {//上传图片mFilePathCallback?.onReceiveValue(arrayOf(Uri.fromFile(file)))}override fun onError(e: Throwable) {mFilePathCallback?.onReceiveValue(null)}})}

最后

如果你看到了这里,觉得文章写得不错就给个赞呗!欢迎大家评论讨论!如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足,定期免费分享技术干货。谢谢!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。