在 Android 上的 React-Native 中压缩 base64 编码图像无法识别 'data' 协议
Compressing base64 encoded images in React-Native on Android does not recognise 'data' protocol
问题
在 React-Native (0.43) 应用程序中,我们使用一个组件,该组件使用 SectionList 呈现按天排序的照片。每个部分可以包含多个图像。照片使用 react-native-image-crop-picker 库拍摄并上传到后端,或者如果没有可用的互联网连接则在本地排队,以 base64 格式编码。图像分辨率设置为 800x800 像素(图像的其他用途的要求)。在内存较低的手机上,渲染 ~20 张图像会因内存不足而导致应用程序崩溃。此问题只能在低端 Android 手机上重现,但我认为这是一个低内存问题,与 OS 无关。为了解决这个问题,需要生成缩略图来测试是否是这种情况。独立于何时生成这些缩略图(在发送到服务器之前或在加载组件之前即时)。下面的代码适用于 iOS 但对于 Android 它会抛出错误: Unknown protocol: data which comes from the ImageEditor.cropImage()函数。
主要 .js 文件的片段
//The mergeReduxWithMeteor function takes care of merging the redux state,
//containing images not yet uploaded to the server,
//and the Meteor data as received by the server.
//No issues here...
helpers.mergeReduxWithMeteor(props.photoStore, props.SynergySummaryReady ? props.SynergyData : [])
//The imageHelper.compressPhoto does the actual compression
//No issues with the promise chain as is.
.then((data) => {
return Promise.all(data.map(imageHelper.compressPhoto))
})
// The remaining functions take care of the formatting for the SectionList.
// No issues here either... :)
.then((data) => {
return helpers.clusterDataByDay(data)
})
//We populate the resulting data in the state that is used for the SectionList
.then((data) => {
this.setState({NotHorusData: data})
})
.catch((error) => console.error(error))
imageHelper.compressphoto()
export function compressPhoto(photo) {
return new Promise((resolve, reject) => {
let imageSize = {
offset: {
x: 0,
y: 0
},
size: {
width: IMAGE_SIZE,
height: IMAGE_SIZE
},
displaySize: {
width: IMAGE_TARGET_SIZE,
height: IMAGE_TARGET_SIZE,
},
resizeMode: 'contain'
}
ImageEditor.cropImage(`data:image/jpeg;base64,${photo.data.userPhoto}`, imageSize, (imageURI) => {
ImageStore.getBase64ForTag(imageURI, (base64Data) => {
resolve({
...photo,
data: {
...photo.data,
userPhoto: base64Data,
}
})
}, (error) => reject(error))
}, (error) => reject(error))
})
}
方法 1:修复 Android
上的数据协议问题
来自 RN 的 Issue on Github 解决了同样的问题,但没有提供解决方案。
方法 2:通过在 Android
上提供 uri 绕过数据协议问题
虽然由于添加了 communication/delay 而不太受欢迎,但另一种方法是通过提供 ImageStore 提供的临时 URI 来避免数据协议问题。请参阅下面针对 Android.
的改编代码
if(Platform.OS === 'android'){
ImageStore.addImageFromBase64(`data:image/jpeg;base64,${photo.data.userPhoto}`, (tempURI) => {
ImageEditor.cropImage(tempURI, imageSize, (imageURI) => {
ImageStore.getBase64ForTag(imageURI, (base64Data) => {
ImageStore.removeImageForTag(tempURI)
resolve({
...photo,
data: {
...photo.data,
userPhoto: base64Data,
}
})
}, (error) => reject(error))
}, (error) => reject(error))
}, (error) => reject(error))
}
不幸的是 ImageStore.addImageFromBase64 在 Android.
上无法识别
有没有人在 Android 上使用过 ImageEditor 和 ImageStore 可能对这种情况有帮助?也欢迎任何其他方法!
我设法使用 react-native-fetch-blob 和 react-native-image-resizer 解决了这个问题对于 iOS 和 Android。与上述问题中的实施相比,性能出乎意料地好。我分享了下面的代码供其他人使用:)
export function compressPhoto(photo) {
return new Promise((resolve, reject) => {
let tempUri = `${cache}/Vire_${photo.data.userPhotoDate}.jpg`
fs.writeFile(tempUri, photo.data.userPhoto, "base64")
.then(() => {
ImageResizer.createResizedImage(
`file:${tempUri}`, IMAGE_TARGET_SIZE, IMAGE_TARGET_SIZE, "JPEG", 100, 0).then((resizedImageUri) => {
fs.readFile(`${resizedImageUri}`, "base64")
.then( data => {
resolve({...photo, data: { ...photo.data, userPhoto: data }})
})
.catch(error => reject(`readFile:error: ${error}`))
},
(error) => reject(`createResizedImage:error: ${error}`)
)
})
.catch( error => {
reject(`writeFile:error: ${error}`)
})
})
}
主要是将base64编码后的图片存放在cache-directory中,然后使用imageResizer获取图片,压缩,再以base64读取使用。
问题
在 React-Native (0.43) 应用程序中,我们使用一个组件,该组件使用 SectionList 呈现按天排序的照片。每个部分可以包含多个图像。照片使用 react-native-image-crop-picker 库拍摄并上传到后端,或者如果没有可用的互联网连接则在本地排队,以 base64 格式编码。图像分辨率设置为 800x800 像素(图像的其他用途的要求)。在内存较低的手机上,渲染 ~20 张图像会因内存不足而导致应用程序崩溃。此问题只能在低端 Android 手机上重现,但我认为这是一个低内存问题,与 OS 无关。为了解决这个问题,需要生成缩略图来测试是否是这种情况。独立于何时生成这些缩略图(在发送到服务器之前或在加载组件之前即时)。下面的代码适用于 iOS 但对于 Android 它会抛出错误: Unknown protocol: data which comes from the ImageEditor.cropImage()函数。
主要 .js 文件的片段
//The mergeReduxWithMeteor function takes care of merging the redux state,
//containing images not yet uploaded to the server,
//and the Meteor data as received by the server.
//No issues here...
helpers.mergeReduxWithMeteor(props.photoStore, props.SynergySummaryReady ? props.SynergyData : [])
//The imageHelper.compressPhoto does the actual compression
//No issues with the promise chain as is.
.then((data) => {
return Promise.all(data.map(imageHelper.compressPhoto))
})
// The remaining functions take care of the formatting for the SectionList.
// No issues here either... :)
.then((data) => {
return helpers.clusterDataByDay(data)
})
//We populate the resulting data in the state that is used for the SectionList
.then((data) => {
this.setState({NotHorusData: data})
})
.catch((error) => console.error(error))
imageHelper.compressphoto()
export function compressPhoto(photo) {
return new Promise((resolve, reject) => {
let imageSize = {
offset: {
x: 0,
y: 0
},
size: {
width: IMAGE_SIZE,
height: IMAGE_SIZE
},
displaySize: {
width: IMAGE_TARGET_SIZE,
height: IMAGE_TARGET_SIZE,
},
resizeMode: 'contain'
}
ImageEditor.cropImage(`data:image/jpeg;base64,${photo.data.userPhoto}`, imageSize, (imageURI) => {
ImageStore.getBase64ForTag(imageURI, (base64Data) => {
resolve({
...photo,
data: {
...photo.data,
userPhoto: base64Data,
}
})
}, (error) => reject(error))
}, (error) => reject(error))
})
}
方法 1:修复 Android
上的数据协议问题 来自 RN 的Issue on Github 解决了同样的问题,但没有提供解决方案。
方法 2:通过在 Android
上提供 uri 绕过数据协议问题虽然由于添加了 communication/delay 而不太受欢迎,但另一种方法是通过提供 ImageStore 提供的临时 URI 来避免数据协议问题。请参阅下面针对 Android.
的改编代码if(Platform.OS === 'android'){
ImageStore.addImageFromBase64(`data:image/jpeg;base64,${photo.data.userPhoto}`, (tempURI) => {
ImageEditor.cropImage(tempURI, imageSize, (imageURI) => {
ImageStore.getBase64ForTag(imageURI, (base64Data) => {
ImageStore.removeImageForTag(tempURI)
resolve({
...photo,
data: {
...photo.data,
userPhoto: base64Data,
}
})
}, (error) => reject(error))
}, (error) => reject(error))
}, (error) => reject(error))
}
不幸的是 ImageStore.addImageFromBase64 在 Android.
上无法识别有没有人在 Android 上使用过 ImageEditor 和 ImageStore 可能对这种情况有帮助?也欢迎任何其他方法!
我设法使用 react-native-fetch-blob 和 react-native-image-resizer 解决了这个问题对于 iOS 和 Android。与上述问题中的实施相比,性能出乎意料地好。我分享了下面的代码供其他人使用:)
export function compressPhoto(photo) {
return new Promise((resolve, reject) => {
let tempUri = `${cache}/Vire_${photo.data.userPhotoDate}.jpg`
fs.writeFile(tempUri, photo.data.userPhoto, "base64")
.then(() => {
ImageResizer.createResizedImage(
`file:${tempUri}`, IMAGE_TARGET_SIZE, IMAGE_TARGET_SIZE, "JPEG", 100, 0).then((resizedImageUri) => {
fs.readFile(`${resizedImageUri}`, "base64")
.then( data => {
resolve({...photo, data: { ...photo.data, userPhoto: data }})
})
.catch(error => reject(`readFile:error: ${error}`))
},
(error) => reject(`createResizedImage:error: ${error}`)
)
})
.catch( error => {
reject(`writeFile:error: ${error}`)
})
})
}
主要是将base64编码后的图片存放在cache-directory中,然后使用imageResizer获取图片,压缩,再以base64读取使用。