Commit a51d9a99 authored by yinjiacheng's avatar yinjiacheng

update 身份证、营业执照上传前压缩优化

parent 0ac4ebf6
package com.yidian.common.utils
import android.content.Context
import android.graphics.Bitmap
import id.zelory.compressor.Compressor
import id.zelory.compressor.constraint.destination
import id.zelory.compressor.constraint.format
import id.zelory.compressor.constraint.quality
import id.zelory.compressor.constraint.resolution
import kotlinx.coroutines.Dispatchers
import java.io.File
/**
* author: yinjiacheng
* date: 7/15/21 10:55 PM
* description: 图片压缩工具
* https://github.com/zetbaitsu/Compressor
*/
object ImageCompressUtils {
/**
* 图片压缩
* @param srcFile 源文件
* @param destFile 目标文件
* @param destWidth 目标宽度
* @param destHeight 目标高度
* @param quality 压缩质量
* @param format 压缩格式
* @param syncOrAsync 同步or异步
*/
suspend fun compress(
context: Context,
srcFile: File,
destFile: File? = null,
destWidth: Int? = null,
destHeight: Int? = null,
quality: Int? = null,
format: Bitmap.CompressFormat = Bitmap.CompressFormat.JPEG,
syncOrAsync: Boolean = false
): File {
return Compressor.compress(context, srcFile, if (syncOrAsync) Dispatchers.Main else Dispatchers.IO) {
destFile?.let { destination(it) }
destWidth?.let { width -> destHeight?.let { height -> resolution(width, height) } }
quality?.let { quality(it) }
format(format)
}
}
}
......@@ -112,4 +112,9 @@ object Constant {
// 服务端返回的业务错误码
const val ERROR_CODE_PERSONAL_AUTH_INFO_DUPLICATE = 2103010 // 身份证号已认证并且认证手机号不是当前手机号
// 身份证、营业执照压缩参数
const val COMPRESS_DEST_WIDTH = 480 // 目标宽度
const val COMPRESS_DEST_HEIGHT = 640 // 目标高度
const val COMPRESS_DEST_QUALITY = 50 // 目标质量
}
......@@ -14,10 +14,12 @@ import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import com.bumptech.glide.Glide
import com.tbruyelle.rxpermissions3.RxPermissions
import com.yidian.common.base.BaseFragment
import com.yidian.common.constant.GlobalConstants
import com.yidian.common.utils.ImageCompressUtils
import com.yidian.framework.mobile.insight.manage.XInsight
import com.yidian.framework.mobile.insight.ocr.BusinessLicenseCallBack
import com.yidian.framework.mobile.insight.ocr.BusinessLicenseResult
......@@ -33,6 +35,7 @@ import com.yidian.shenghuoquan.newscontent.utils.FileUtil
import com.yidian.shenghuoquan.newscontent.utils.InputFilterUtil
import com.yidian.shenghuoquan.newscontent.widget.LifeAccountAuthBusinessInfoEditView
import com.yidian.utils.ToastUtil
import kotlinx.coroutines.launch
import java.io.File
/**
......@@ -214,11 +217,20 @@ class LifeAccountBusinessLicenseAuthFragmentV2 :
*/
private fun startBusinessLicenseOCR() {
// 上传前压缩图像
BitmapUtil.compressImage(
commonPath + Constant.FILE_PATH_BUSINESS_LICENSE,
10
)
XInsight.getBusinessLicenseOcrInfo(File(commonPath + Constant.FILE_PATH_BUSINESS_LICENSE), this)
lifecycleScope.launch {
activity?.let {
ImageCompressUtils.compress(
it,
File(commonPath + Constant.FILE_PATH_BUSINESS_LICENSE),
File(commonPath + Constant.FILE_PATH_BUSINESS_LICENSE),
Constant.COMPRESS_DEST_WIDTH, Constant.COMPRESS_DEST_HEIGHT, Constant.COMPRESS_DEST_QUALITY
)
}
XInsight.getBusinessLicenseOcrInfo(
File(commonPath + Constant.FILE_PATH_BUSINESS_LICENSE),
this@LifeAccountBusinessLicenseAuthFragmentV2
)
}
}
/**
......
......@@ -12,8 +12,10 @@ import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import com.tbruyelle.rxpermissions3.RxPermissions
import com.yidian.common.base.BaseFragment
import com.yidian.common.utils.ImageCompressUtils
import com.yidian.framework.mobile.insight.config.IdentityCardType
import com.yidian.framework.mobile.insight.config.ScreenOrientation
import com.yidian.framework.mobile.insight.faceid.LiveVerifyCallBack
......@@ -43,6 +45,9 @@ import com.yidian.shenghuoquan.newscontent.widget.LifeAccountAuthImageView
import com.yidian.shenghuoquan.newscontent.widget.LifeAccountFaceAuthView
import com.yidian.shenghuoquan.newscontent.widget.PrivacyAgreementView
import com.yidian.utils.ToastUtil
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
/**
......@@ -292,10 +297,6 @@ class LifeAccountIDCardAuthFragmentV2 : BaseFragment<FragmentLifeAccountIdCardAu
data?.data,
cachePath + Constant.FILE_PATH_ID_CARD_PORTRAIT_FACE
)
BitmapUtil.compressImage(
cachePath + Constant.FILE_PATH_ID_CARD_PORTRAIT_FACE,
10
)
startIDCardVerify(Constant.ID_CARD_PORTRAIT_FACE)
} else {
val bitmap = BitmapUtil.generateBitmapFromUri(
......@@ -309,10 +310,6 @@ class LifeAccountIDCardAuthFragmentV2 : BaseFragment<FragmentLifeAccountIdCardAu
data?.data,
cachePath + Constant.FILE_PATH_ID_CARD_NATIONAL_EMBLEM_FACE
)
BitmapUtil.compressImage(
cachePath + Constant.FILE_PATH_ID_CARD_NATIONAL_EMBLEM_FACE,
10
)
startIDCardVerify(Constant.ID_CARD_NATIONAL_EMBLEM_FACE)
}
}
......@@ -357,65 +354,85 @@ class LifeAccountIDCardAuthFragmentV2 : BaseFragment<FragmentLifeAccountIdCardAu
* @param face 身份证两面标识
*/
private fun startIDCardVerify(face: Int) {
val uploadFile = if (face == Constant.ID_CARD_PORTRAIT_FACE) {
File(cachePath + Constant.FILE_PATH_ID_CARD_PORTRAIT_FACE)
} else {
File(cachePath + Constant.FILE_PATH_ID_CARD_NATIONAL_EMBLEM_FACE)
}
XInsight.identityCardVerify(
if (face == Constant.ID_CARD_PORTRAIT_FACE) IdentityCardType.PORTRAIT else IdentityCardType.NATIONAL_EMBLEM,
uploadFile,
object : IdentityCardVerifyCallBack {
override fun onSucceed(identityCardResult: IdentityCardResult?, result: String?) {
activity?.runOnUiThread {
if (face == Constant.ID_CARD_PORTRAIT_FACE) {
identityCardResult?.let { LifeAccountAuthDataManagerV2.generateIDCardPortraitFaceData(it) }
// 回显OCR结果
viewBinding.evRealName.fillEditContent(identityCardResult?.posit?.name)
viewBinding.evIdCardNumber.fillEditContent(identityCardResult?.posit?.idcard_number)
// 此时身份证人像面已上传并OCR识别完成 删除本地临时存储文件
File(cachePath + Constant.FILE_PATH_ID_CARD_PORTRAIT_FACE).apply {
if (exists()) delete()
}
// 检查下一步条件
if (authType == Constant.TYPE_AUTH_PERSONAL) {
activity?.let { (it as LifeAccountPersonalAuthActivity).checkNextCondition() }
} else {
activity?.let { (it as LifeAccountEnterpriseAuthActivity).checkNextCondition() }
}
} else {
identityCardResult?.let { LifeAccountAuthDataManagerV2.generateIDCardNationEmblemFaceData(it) }
// 此时身份证国徽面已上传并OCR识别完成 删除本地临时存储文件
File(cachePath + Constant.FILE_PATH_ID_CARD_NATIONAL_EMBLEM_FACE).apply {
if (exists()) delete()
}
// 检查下一步条件
if (authType == Constant.TYPE_AUTH_PERSONAL) {
activity?.let { (it as LifeAccountPersonalAuthActivity).checkNextCondition() }
lifecycleScope.launch {
// 压缩图片
val uploadFile = withContext(Dispatchers.IO) {
if (face == Constant.ID_CARD_PORTRAIT_FACE) {
activity?.let {
ImageCompressUtils.compress(
it,
File(cachePath + Constant.FILE_PATH_ID_CARD_PORTRAIT_FACE),
File(cachePath + Constant.FILE_PATH_ID_CARD_PORTRAIT_FACE),
Constant.COMPRESS_DEST_WIDTH, Constant.COMPRESS_DEST_HEIGHT, Constant.COMPRESS_DEST_QUALITY
)
}
File(cachePath + Constant.FILE_PATH_ID_CARD_PORTRAIT_FACE)
} else {
activity?.let {
ImageCompressUtils.compress(
it,
File(cachePath + Constant.FILE_PATH_ID_CARD_NATIONAL_EMBLEM_FACE),
File(cachePath + Constant.FILE_PATH_ID_CARD_NATIONAL_EMBLEM_FACE),
Constant.COMPRESS_DEST_WIDTH, Constant.COMPRESS_DEST_HEIGHT, Constant.COMPRESS_DEST_QUALITY
)
}
File(cachePath + Constant.FILE_PATH_ID_CARD_NATIONAL_EMBLEM_FACE)
}
}
XInsight.identityCardVerify(
if (face == Constant.ID_CARD_PORTRAIT_FACE) IdentityCardType.PORTRAIT else IdentityCardType.NATIONAL_EMBLEM,
uploadFile,
object : IdentityCardVerifyCallBack {
override fun onSucceed(identityCardResult: IdentityCardResult?, result: String?) {
activity?.runOnUiThread {
if (face == Constant.ID_CARD_PORTRAIT_FACE) {
identityCardResult?.let { LifeAccountAuthDataManagerV2.generateIDCardPortraitFaceData(it) }
// 回显OCR结果
viewBinding.evRealName.fillEditContent(identityCardResult?.posit?.name)
viewBinding.evIdCardNumber.fillEditContent(identityCardResult?.posit?.idcard_number)
// 此时身份证人像面已上传并OCR识别完成 删除本地临时存储文件
File(cachePath + Constant.FILE_PATH_ID_CARD_PORTRAIT_FACE).apply {
if (exists()) delete()
}
// 检查下一步条件
if (authType == Constant.TYPE_AUTH_PERSONAL) {
activity?.let { (it as LifeAccountPersonalAuthActivity).checkNextCondition() }
} else {
activity?.let { (it as LifeAccountEnterpriseAuthActivity).checkNextCondition() }
}
} else {
activity?.let { (it as LifeAccountEnterpriseAuthActivity).checkNextCondition() }
identityCardResult?.let { LifeAccountAuthDataManagerV2.generateIDCardNationEmblemFaceData(it) }
// 此时身份证国徽面已上传并OCR识别完成 删除本地临时存储文件
File(cachePath + Constant.FILE_PATH_ID_CARD_NATIONAL_EMBLEM_FACE).apply {
if (exists()) delete()
}
// 检查下一步条件
if (authType == Constant.TYPE_AUTH_PERSONAL) {
activity?.let { (it as LifeAccountPersonalAuthActivity).checkNextCondition() }
} else {
activity?.let { (it as LifeAccountEnterpriseAuthActivity).checkNextCondition() }
}
}
}
}
}
override fun onFail(code: Int, errMsg: String?, errDomain: String?) {
override fun onFail(code: Int, errMsg: String?, errDomain: String?) {
}
override fun onUploadTask(positImageUrl: String?, backImageUrl: String?) {
if (face == Constant.ID_CARD_PORTRAIT_FACE) {
LifeAccountAuthDataManagerV2.personalAuthData.isIDCardPortraitFaceUpload =
true
LifeAccountAuthDataManagerV2.personalAuthData.idCardPortraitFaceUrl = positImageUrl
} else {
LifeAccountAuthDataManagerV2.personalAuthData.isIDCardNationalEmblemFaceUpload =
true
LifeAccountAuthDataManagerV2.personalAuthData.idCardNationalEmblemFaceUrl = backImageUrl
}
}
})
override fun onUploadTask(positImageUrl: String?, backImageUrl: String?) {
if (face == Constant.ID_CARD_PORTRAIT_FACE) {
LifeAccountAuthDataManagerV2.personalAuthData.isIDCardPortraitFaceUpload =
true
LifeAccountAuthDataManagerV2.personalAuthData.idCardPortraitFaceUrl = positImageUrl
} else {
LifeAccountAuthDataManagerV2.personalAuthData.isIDCardNationalEmblemFaceUpload =
true
LifeAccountAuthDataManagerV2.personalAuthData.idCardNationalEmblemFaceUrl = backImageUrl
}
}
})
}
}
override fun authPersonCheckSuccess() {
......
......@@ -16,58 +16,60 @@ import java.io.FileOutputStream
*/
object BitmapUtil {
/**
* 从Uri生成Bitmap
* @param container bitmap容器
*/
fun generateBitmapFromUri(context: Context?, uri: Uri?, container: View): Bitmap? {
if (uri == null) return null
val parcelFileDescriptor = context?.contentResolver?.openFileDescriptor(uri, "r")
val options = BitmapFactory.Options()
options.inJustDecodeBounds = true
BitmapFactory.decodeFileDescriptor(parcelFileDescriptor?.fileDescriptor, null, options)
// 尺寸压缩
val sourceWidth = options.outWidth
val sourceHeight = options.outHeight
var inSampleSize = 1
if (sourceWidth > container.width || sourceHeight > container.height) {
val halfWidth = sourceWidth / 2
val halfHeight = sourceHeight / 2
// 压缩后的尺寸与所需的尺寸进行比较
while (halfWidth / inSampleSize >= container.width && halfHeight / inSampleSize >= container.height) {
inSampleSize *= 2
}
}
options.inSampleSize = inSampleSize
options.inJustDecodeBounds = false
val bitmap =
BitmapFactory.decodeFileDescriptor(parcelFileDescriptor?.fileDescriptor, null, options)
parcelFileDescriptor?.close()
return bitmap
/**
* 从Uri生成Bitmap
* @param container bitmap容器
*/
fun generateBitmapFromUri(context: Context?, uri: Uri?, container: View): Bitmap? {
if (uri == null) return null
val parcelFileDescriptor = context?.contentResolver?.openFileDescriptor(uri, "r")
val options = BitmapFactory.Options()
options.inJustDecodeBounds = true
BitmapFactory.decodeFileDescriptor(parcelFileDescriptor?.fileDescriptor, null, options)
// 尺寸压缩
val sourceWidth = options.outWidth
val sourceHeight = options.outHeight
var inSampleSize = 1
if (sourceWidth > container.width || sourceHeight > container.height) {
val halfWidth = sourceWidth / 2
val halfHeight = sourceHeight / 2
// 压缩后的尺寸与所需的尺寸进行比较
while (halfWidth / inSampleSize >= container.width && halfHeight / inSampleSize >= container.height) {
inSampleSize *= 2
}
}
options.inSampleSize = inSampleSize
options.inJustDecodeBounds = false
val bitmap =
BitmapFactory.decodeFileDescriptor(parcelFileDescriptor?.fileDescriptor, null, options)
parcelFileDescriptor?.close()
return bitmap
}
/**
* 从Bitmap生成File
* @param quality 压缩质量
* @param destPath 目标文件全路径
*/
fun generateFileFromBitmap(bitmap: Bitmap?, quality: Int, destPath: String) {
val file = File(destPath)
if (file.exists()) file.delete()
file.parentFile?.mkdirs()
file.createNewFile()
bitmap?.compress(Bitmap.CompressFormat.JPEG, quality, FileOutputStream(destPath))
}
/**
* 从Bitmap生成File
* @param quality 压缩质量
* @param destPath 目标文件全路径
*/
fun generateFileFromBitmap(bitmap: Bitmap?, quality: Int, destPath: String) {
val file = File(destPath)
if (file.exists()) file.delete()
if (file.parentFile?.exists() == false) file.parentFile?.mkdirs()
file.createNewFile()
val outputStream = FileOutputStream(destPath)
bitmap?.compress(Bitmap.CompressFormat.JPEG, quality, outputStream)
outputStream.close()
}
/**
* 图片压缩
* @param path 将要压缩的图片圈路径,同时也是压缩完成后的图片 即覆盖原图片
* @param quality 压缩质量
*/
fun compressImage(path: String, quality: Int) {
if (!File(path).exists()) throw FileNotFoundException("compress file not exist!")
BitmapFactory.decodeFile(path)
.compress(Bitmap.CompressFormat.JPEG, quality, FileOutputStream(path))
}
/**
* 图片压缩
* @param path 将要压缩的图片圈路径,同时也是压缩完成后的图片 即覆盖原图片
* @param quality 压缩质量
*/
fun compressImage(path: String, quality: Int) {
if (!File(path).exists()) throw FileNotFoundException("compress file not exist!")
BitmapFactory.decodeFile(path)
.compress(Bitmap.CompressFormat.JPEG, quality, FileOutputStream(path))
}
}
\ No newline at end of file
}
......@@ -112,7 +112,9 @@ ext.dependencies = [
//https://github.com/CymChad/BaseRecyclerViewAdapterHelper
'com.github.CymChad:BaseRecyclerViewAdapterHelper:3.0.4',
// PictureSelector2.0 https://github.com/LuckSiege/PictureSelector
'io.github.lucksiege:pictureselector:v2.7.3-rc05'
'io.github.lucksiege:pictureselector:v2.7.3-rc05',
// Compressor https://github.com/zetbaitsu/Compressor
'id.zelory:compressor:3.0.1'
],
other : [
':CommonLib:Common'
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment