RN 图片拍照上传

115 阅读2分钟
import { ImagePickerResponse, launchCamera, launchImageLibrary } from 'react-native-image-picker'
import { getBoolean } from './storage'
import navigationUtils from './navigationUtils'
import { RouteName, StorageType } from '@/types'
import PrivilegeUtil from './PrivilegeUtil'
import ImageResizer from '@bam.tech/react-native-image-resizer'
import RNFS from 'react-native-fs'
import { formatError } from '.'
import { Platform } from 'react-native'

// 创建一个回调函数存储器
let imageCallback: ((result: ImagePickerResponse) => void) | null = null

// 设置回调函数
export const setImageCallback = (callback: (result: ImagePickerResponse) => void) => {
	imageCallback = callback
}

const isIos = Platform.OS === 'ios'

// 清除回调函数
export const clearImageCallback = () => {
	imageCallback = null
}

// 处理照片结果
export const handlePhotoResult = ({
	photo,
	width,
	height,
	base64,
}: {
	photo: string
	width: number
	height: number
	base64: string
}) => {
	if (imageCallback) {
		const fileName = photo.split('/')?.pop()
		const result: ImagePickerResponse = {
			assets: [
				{
					base64,
					uri: photo,
					type: 'image/jpeg',
					fileName,
					width,
					height,
				},
			],
		}
		imageCallback(result)
		clearImageCallback()
	}
}

export const chooseImage = async (
	accept: Array<'photo' | 'camera'>,
	isMultiple = false
): Promise<ImagePickerResponse> => {
	if (accept.length === 1) {
		const type = accept[0]
		const result = type === 'camera' ? await handlelaunchCamera() : await handleLaunchImageLibrary(isMultiple)

		const { uri, width, height, fileSize, fileName } = result?.assets?.[0] ?? {}

		handleEventTracking({
			tip: '完成拍照,即将上传--chooseImage.ts--67',
			data: {
				fileInfo: {
					uri,
					width,
					height,
					fileSize,
					fileName,
				},
			},
		})

		return result
	} else {
		return new Promise((resolve, reject) => {
			const modalId = NormalModal.ActionSheet({
				options: ['拍摄', '从相册选择'],
				autoCloseModal: false,
				onSelect: (_, index) => {
					NormalModal.hide(modalId)
					const result = index === 0 ? handlelaunchCamera() : handleLaunchImageLibrary(isMultiple)
					result.then(resolve, reject)
				},
			})
		})
	}
}

/*** 相册 */
const handleLaunchImageLibrary = async (isMultiple: boolean): Promise<ImagePickerResponse> => {
	const result: ImagePickerResponse = await launchImageLibrary({
		mediaType: 'photo',
		selectionLimit: isMultiple ? 0 : 1,
		includeBase64: false,
		quality: isIos ? 0.6 : 0.8,
		maxWidth: 1440,
		maxHeight: 1440,
		presentationStyle: 'fullScreen',
	})

	return handleCameraResult(result)
}

/*** 相机 */
const handlelaunchCamera = async (): Promise<ImagePickerResponse> => {
	const permission = await PrivilegeUtil.checkAndRequestCameraPermission()
	let result: ImagePickerResponse = {}
	if (!permission) return result
	const customcamera = getBoolean(StorageType.CUSTOMCAMERA) || false

	if (customcamera) {
		// 将 Promise 包装的结果返回
		result = await new Promise<ImagePickerResponse>((resolve) => {
			setImageCallback(resolve)
			navigationUtils.navigate(RouteName.CustomCamera)
		})
	} else {
		result = await launchCamera({
			mediaType: 'photo',
			cameraType: 'back',
			includeBase64: false,
			quality: isIos ? 0.6 : 0.8,
			maxWidth: 1440,
			maxHeight: 1440,
			presentationStyle: 'overFullScreen',
		})
	}
	return await handleCameraResult(result)
}

/*** 处理相机的照片 */
const handleCameraResult = async (result: ImagePickerResponse) => {
	const newResult = result
	try {
		if (newResult?.assets?.[0]?.uri) {
			const response = await clearImageExif(newResult.assets[0].uri)
			if (response) {
				newResult.assets[0].base64 = response.base64String
				newResult.assets[0].uri = response.uri
				newResult.assets[0].fileSize = response.size
				newResult.assets[0].width = response.width
				newResult.assets[0].height = response.height
			} else {
				const { uri } = newResult.assets[0]
				const base64 = await imageConversionBase64(uri)
				if (base64) {
					newResult.assets[0].base64 = base64
				} else {
					handleEventTracking({
						tip: '本地图片转换base64地址失败--chooseImage.ts--141',
						data: {
							base64: base64,
							uri,
						},
					})
				}
			}
		}
	} catch (error) {}
	return newResult
}

/*** 去除图片的 exif 信息 */
export const clearImageExif = async (uri: string) => {
	try {
		if (isIos) return
		const response = await ImageResizer.createResizedImage(uri, 1440, 1440, 'JPEG', 80, 0, null, false)
		const base64String = await imageConversionBase64(response.uri)
		return {
			...response,
			base64String,
		}
	} catch (error) {
		handleEventTracking({
			tip: '去除图片原数据失败--chooseImage.ts--130',
			data: {
				errorObj: formatError(error),
				uri,
			},
		})
	}
}

/*** 图片转换成base64 */
const imageConversionBase64 = async (uri: string) => {
	try {
		const base64String = await RNFS.readFile(uri, 'base64')
		return base64String
	} catch (error) {
		handleEventTracking({
			tip: '本地图片转换base64失败--chooseImage.ts--139',
			data: {
				errorObj: formatError(error),
				uri,
			},
		})

		return ''
	}
}