webview上传图片开发部分代码记录

这两天在开发公司的一个自营商城的功能,需要配合前端的H5的开发。
中间涉及到webview的图片选择上传的功能,在这里做一下记录。
使用了Kotlin进行的开发,如有看不懂的可以评论回复。
1.图片上传会涉及到图片选择和拍照功能。相关代码如下:

//拍照代码 private fun openCamera() { imagePaths = FileManager.imageCacheDir().absolutePath + "${System.currentTimeMillis()}.jpg" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { doTakePhotoIn7() } else { var intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE); // 必须确保文件夹路径存在,否则拍照后无法完成回调 var vFile = File(imagePaths) if (!vFile.exists()) { vFile.mkdirs() } else { if (vFile.exists()) { vFile.delete() } } cameraUri = Uri.fromFile(vFile) intent.putExtra(MediaStore.EXTRA_OUTPUT, cameraUri) startActivityForResult(intent, Short.MAX_VALUE / 3); } }

//选择图片 private fun toChoicePic() { var i = Intent(action) i?.addCategory(Intent.CATEGORY_OPENABLE) i?.type = "image/*" startActivityForResult(Intent.createChooser(i, "Image Chooser"), Short.MAX_VALUE / 2) }

2.webview的相关处理:
webView.webChromeClient = object : WebChromeClient() { private var mView: View? = null/** * 全屏处理 */ override fun onShowCustomView(view: View?, callback: CustomViewCallback?) { super.onShowCustomView(view, callback) val parent = webView.parent as ViewGroup parent.removeView(webView) // 设置背景色为黑色 view?.setBackgroundColor(Color.BLACK) parent.addView(view) mView = view setFullScreen() }/** * 退出全屏 */ override fun onHideCustomView() { super.onHideCustomView() if (mView != null) { val parent = mView?.parent as ViewGroup parent.removeView(mView) parent.addView(webView) mView = null quitFullScreen() } }// For Android < 3.0 fun openFileChooser(valueCallback: ValueCallback) { openFileChooser(valueCallback, "", "") }// For Android>= 3.0 fun openFileChooser(valueCallback: ValueCallback, acceptType: String) { openFileChooser(valueCallback, acceptType, "") }//For Android>= 4.1 fun openFileChooser(valueCallback: ValueCallback, acceptType: String, capture: String) { if (uploadMessage != null) { uploadMessage = null } uploadMessage = valueCallback; toselectPicture() }override fun onShowFileChooser(webView: WebView?, filePathCallback: ValueCallback>?, fileChooserParams: FileChooserParams?): Boolean { if (uploadMessageAboveL != null) { uploadMessageAboveL = null } uploadMessageAboveL = filePathCallback; toselectPicture() return true } }

3.因为图片要求在1M以内,所以需要对图片进行相关压缩处理:
/** * 对图片进行压缩处理 */ private fun toCompressPic(srcPath: String?): Bitmap { val newOpts = BitmapFactory.Options() // 开始读入图片,此时把options.inJustDecodeBounds 设回true了 newOpts.inJustDecodeBounds = true var bitmap = BitmapFactory.decodeFile(srcPath, newOpts)// 此时返回bm为空newOpts.inJustDecodeBounds = false val w = newOpts.outWidth val h = newOpts.outHeight // 现在主流手机比较多是800*480分辨率,所以高和宽我们设置为 val hh = 800f// 这里设置高度为800f val ww = 480f// 这里设置宽度为480f // 缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可 var be = 1// be=1表示不缩放 if (w > h && w > ww) {// 如果宽度大的话根据宽度固定大小缩放 be = (newOpts.outWidth / ww).toInt() } else if (w < h && h > hh) {// 如果高度高的话根据宽度固定大小缩放 be = (newOpts.outHeight / hh).toInt() } if (be <= 0) be = 1 newOpts.inSampleSize = be// 设置缩放比例 // 重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了 bitmap = BitmapFactory.decodeFile(srcPath, newOpts) return compressImage(bitmap)// 压缩好比例大小后再进行质量压缩 }private fun compressImage(image: Bitmap): Bitmap { var baos = ByteArrayOutputStream() image.compress(Bitmap.CompressFormat.JPEG, 100, baos); // 质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中 var options = 100; while (baos.toByteArray().size / 1024 > 1024) { // 循环判断如果压缩后图片是否大于100kb,大于继续压缩 baos.reset(); // 重置baos即清空baos image.compress(Bitmap.CompressFormat.JPEG, options, baos); // 这里压缩options%,把压缩后的数据存放到baos中 options -= 10; // 每次都减少10 } var isBm = ByteArrayInputStream(baos.toByteArray()); // 把压缩后的数据baos存放到ByteArrayInputStream中 var bitmap = BitmapFactory.decodeStream(isBm, null, null); // 把ByteArrayInputStream数据生成图片 return bitmap; }

【webview上传图片开发部分代码记录】4.相关压缩过程中需要涉及到Uri与文件地址之间的转换,方式如下:
/** * 功能简述:4.4及以上获取图片的方法 * 功能详细描述: * 注意: * @param context * @param uri * @return */ @RequiresApi(Build.VERSION_CODES.KITKAT) @TargetApi(Build.VERSION_CODES.KITKAT) fun getPath(context: Context, uri: Uri): String? {val isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT// DocumentProvider if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) { // ExternalStorageProvider if (isExternalStorageDocument(uri)) { val docId = DocumentsContract.getDocumentId(uri) val split = docId.split(":".toRegex()).dropLastWhile({ it.isEmpty() }).toTypedArray() val type = split[0]if ("primary".equals(type, ignoreCase = true)) { return "${Environment.getExternalStorageDirectory()}/${split[1]}" } } else if (isDownloadsDocument(uri)) {val id = DocumentsContract.getDocumentId(uri) val contentUri = ContentUris.withAppendedId( Uri.parse("content://downloads/public_downloads"), java.lang.Long.valueOf(id))return getDataColumn(context, contentUri, null, null) } else if (isMediaDocument(uri)) { val docId = DocumentsContract.getDocumentId(uri) val split = docId.split(":".toRegex()).dropLastWhile({ it.isEmpty() }).toTypedArray() val type = split[0]var contentUri: Uri? = null if ("image" == type) { contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI } else if ("video" == type) { contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI } else if ("audio" == type) { contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI }val selection = "_id=?" val selectionArgs = arrayOf(split[1])return getDataColumn(context, contentUri, selection, selectionArgs) }// MediaProvider // DownloadsProvider } else if ("content".equals(uri.scheme, ignoreCase = true)) {// Return the remote address return if (isGooglePhotosUri(uri)) uri.lastPathSegment else getDataColumn(context, uri, null, null)} else if ("file".equals(uri.scheme, ignoreCase = true)) { return uri.path }// File // MediaStore (and general)return null }fun getDataColumn(context: Context, uri: Uri?, selection: String?, selectionArgs: Array?): String? {var cursor: Cursor? = null val column = "_data" val projection = arrayOf(column)try { cursor = context.contentResolver.query(uri!!, projection, selection, selectionArgs, null) if (cursor != null && cursor!!.moveToFirst()) { val 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. */ fun isExternalStorageDocument(uri: Uri): Boolean { return "com.android.externalstorage.documents" == uri.authority }/** * @param uri The Uri to check. * @return Whether the Uri authority is DownloadsProvider. */ fun isDownloadsDocument(uri: Uri): Boolean { return "com.android.providers.downloads.documents" == uri.authority }/** * @param uri The Uri to check. * @return Whether the Uri authority is MediaProvider. */ fun isMediaDocument(uri: Uri): Boolean { return "com.android.providers.media.documents" == uri.authority }/** * @param uri The Uri to check. * @return Whether the Uri authority is Google Photos. */ fun isGooglePhotosUri(uri: Uri): Boolean { return "com.google.android.apps.photos.content" == uri.authority }/** * Gets the content:// URIfrom the given corresponding path to a file * @param context * @param imageFile * @return content Uri */ fun getImageContentUri(context: Context, imageFile: File): Uri? { var filePath = imageFile.absolutePath var cursor = context.contentResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, Array(1) { MediaStore.Images.Media._ID }, MediaStore.Images.Media.DATA + "=? ", Array(1) { filePath }, null); if (cursor != null && cursor.moveToFirst()) { var id = cursor.getInt(cursor .getColumnIndex(MediaStore.MediaColumns._ID)); var baseUri = Uri.parse("content://media/external/images/media"); return Uri.withAppendedPath(baseUri, "" + id); } else { if (imageFile.exists()) { var values = ContentValues() values.put(MediaStore.Images.Media.DATA, filePath); return context.contentResolver.insert( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); } else { return null } } }

5.onActivityResult方法内容如下:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (Short.MAX_VALUE / 2 == requestCode) { if (Activity.RESULT_OK == resultCode) { if (uploadMessageAboveL != null) { // 5.0 以上 onActivityResultAboveL(requestCode, resultCode, data) } else if (uploadMessage != null) { // 5.0 以下 uploadMessage?.onReceiveValue(Uri.parse((data?.getSerializableExtra(PictureSelector.KEY_SELECTED_IMAGE) as? Image)?.path)) uploadMessage = null } } } else { refreshPhotoList(imagePaths)//刷新相册,防止取不到图片 var afterUri = Uri.parse(MediaStore.Images.Media.insertImage( context.contentResolver, toCompressPic(imagePaths), null, null)) if (uploadMessageAboveL != null && afterUri != null) { // 5.0 以上 uploadMessageAboveL?.onReceiveValue(Array(1) { afterUri!! }) uploadMessageAboveL = null } else if (uploadMessage != null) { // 5.0 以下 uploadMessage?.onReceiveValue(afterUri) uploadMessage = null } else { cancelSelect() } } if (Activity.RESULT_CANCELED == resultCode) { cancelSelect() } }@TargetApi(Build.VERSION_CODES.LOLLIPOP) fun onActivityResultAboveL(requestCode: Int, resultCode: Int, data: Intent?) { var rest: Array? = null if (data != null) { var dataString = data.dataString var clipData = https://www.it610.com/article/data.clipData if (clipData != null) { rest = Array(clipData.itemCount) { Uri.parse("") } for (i in 0 until clipData.itemCount) { var item = clipData.getItemAt(i) rest[i] = item.uri } } if (dataString != null) { //将Uri获取绝对地址 var contentToPath = getPath(context, Uri.parse(dataString)) //将选定图片进行压缩之后保存到相册并返回对应的uri var afterUri = Uri.parse(MediaStore.Images.Media.insertImage( context.contentResolver, toCompressPic(contentToPath), null, null)) //通过对应的uri获取压缩之后的绝对地址路径 var path2 = getPath(context, afterUri) //刷新相册 refreshPhotoList(path2) //将绝对地址路径转换成content://形式的Uri路径 var finalContentPath = getImageContentUri(context, File(path2)) if (finalContentPath != null) rest = Array(1) { finalContentPath!! } } } uploadMessageAboveL?.onReceiveValue(rest) uploadMessageAboveL = null }/** * 获取照片结束后 */ private fun refreshPhotoList(path:String?) { var f = File(path) addImageGallery(f) }/** 解决拍照或保存图片到相册后在相册中找不到的问题 */ private fun addImageGallery(file: File) { var values = ContentValues() values.put(MediaStore.Images.Media.DATA, file.absolutePath) values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg") context.contentResolver.insert( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values) }/** * 注意:取消选取图片必须调用此方法,否则第二次选择会失效 * 处理重复选取图片问题 */ private fun cancelSelect() { if (uploadMessageAboveL != null) { uploadMessageAboveL?.onReceiveValue(null) uploadMessageAboveL = null } if (uploadMessage != null) { uploadMessage?.onReceiveValue(null) uploadMessage = null } }

    推荐阅读