鸿蒙-保存图片或分享图片功能的开发总结
一、背景
在软件开发过程中,图片保存至本地是一项常见且关键的任务。鉴于图片存在 Base64、pixelmap、arrayBuffer 等多种形式,同时类似微信、微博分享场景中图片保存需求也颇为相似,所以我们着手对相关功能进行优化。通过封装通用性函数(可按需改造用于单张或多张图片),保障图片能稳定存储在本地缓存路径,方便后续随时调用。
同时保存一张或者多张图片适用,需要稍加改造
代码已脱敏,通用性函数封装
二、base64转化PixelMap
当我们收到一个Base64的字符串数据时候,以下是base64转化未pixelMap封装的方法,核心语法使用util.Base64Helper实现,需要注意的是,使用decodeSync对base64字符串解码时,传入的base64字符串不能有’data:image/jpeg;base64,'这样的前缀,则需要转化base64.replace(/^data:image/\w+;base64,/i, ‘’)。
// base64转PixelMap:
export const base64ToPixelMap = async (base64:string)=>{
const str = base64.replace(/^data:image\/\w+;base64,/i, '');
let helper = new util.Base64Helper();
let buffer: ArrayBuffer = helper.decodeSync(str, util.Type.MIME).buffer as ArrayBuffer;
let imageSource = image.createImageSource(buffer);
let opts: image.DecodingOptions = { editable: true };
let pixelMap = await imageSource.createPixelMap(opts);
console.log('base64ToPixelMap',pixelMap)
return pixelMap
}
三、PixelMap转化ArrayBuffer
当我们拿到的是PixeIMap资源得到 ArrayBuffer,实现的API是创建ImagePacker实例。使用packing将图像压缩或重新打包,使用Promise形式返回结果Promise 。
/**
* 将图片打包成ArrayBuffer
* @param pixmap
* @returns
*/
export const packingFn = async (pixmap: image.PixelMap | undefined): Promise<ArrayBuffer> => {
const imagePackerApi = image.createImagePacker();
let packOpts: image.PackingOption = { format: "image/jpeg", quality: 98 };
let dataBuffer: ArrayBuffer = new ArrayBuffer(0)
await imagePackerApi.packing(pixmap, packOpts)
.then((data: ArrayBuffer) => {
dataBuffer = data
}).catch((error: BusinessError) => {
console.error('Failed to pack the image. And the error is: ' + error);
})
return dataBuffer
}
PixelMap使用注意事项:
需要注意的是:
1.在使用微信分享的时候,PixeIMap资源有长度限制100KB。转化成ArrayBuffer处理更佳
四、ArrayBuffer存储在缓存目录
传入ArrayBuffer,返回缓存目录地址,便于后续使用
使用@ohos.file.fileuri (文件URI)
/**
* buffer放在沙箱路径中(缓存目录)
* @param buffer
* @returns 获取图片沙箱路径
*/
export const returnPath = async (buffer: ArrayBuffer): Promise<string> => { // 传入buffer,获取图片沙箱路径 this.pixmapUri
//保存到沙箱中并存储拍摄的文件路径集--用来打开指定的图片 'file://media/Photo//IMG_datetime_0001/displayName.jpg'
const path = getContext().cacheDir + '/Photo' + `${Date.now()}baicizhan${Math.random()}IMG.jpg`
// this.path = path
const newFileUri = fileUri.getUriFromPath(path);
console.log('newFileUri', newFileUri)
try {
const value = await fs.open(path, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE)
fs.writeSync(value.fd, buffer)
fs.closeSync(value)
return newFileUri || ''
} catch (e) {
return ''
}
}
五、沙箱地址下载本地图库
传入沙箱路径,使用photoAccessHelper.getPhotoAccessHelper,获取相册管理模块的实例,用于访问和修改相册中的媒体文件。phAccessHelper.showAssetsCreationDialog 则需要用户授权是否保存。取消则返回[],确认则返回Array,调用createAssetByIo就直接写入本地图库。
export const downLoadImg = async (pixmapUris: string[], fn?: (str?: string) => void) => {
try {
const phAccessHelper = photoAccessHelper.getPhotoAccessHelper(getContext()) //获取相册管理模块的实例,用于访问和修改相册中的媒体文件。
const srcFileUris = pixmapUris
let phCreationConfig: Array<photoAccessHelper.PhotoCreationConfig> = []
pixmapUris.forEach((item: string, index: number) => {
phCreationConfig.push({
title: 'dowmload' + index, // 可选
fileNameExtension: "jpg",
photoType: photoAccessHelper.PhotoType.IMAGE,
// 可选,支持:普通图片、动态图片
subtype: photoAccessHelper.PhotoSubtype.MOVING_PHOTO,
})
})
try {
const desFileUris: Array<string> =
await phAccessHelper.showAssetsCreationDialog(srcFileUris, phCreationConfig) // 权限确认
desFileUris.forEach((item: string, index: number) => {
createAssetByIo(pixmapUris[index], desFileUris[index], desFileUris.length - 1 === index ? true : false,
fn) //生成图片
})
if(desFileUris.length === 0){
console.log('取消resCode')
}
} catch (err) {
console.error('showAssetsCreationDialog failed, errCode is 1' + err.code + ', errMsg is ' + err.message);
}
} catch (err) {
console.error('showAssetsCreationDialog failed, errCode is ' + err.code + ', errMsg is ' + err.message);
}
}
createAssetByIo写入本地图库
export const createAssetByIo =
async (sourceFilePath: string, targetFilePath: string, isFinally: Boolean, fn?: (str: string) => void) => {
try {
let srcFile: fs.File = fs.openSync(sourceFilePath, fs.OpenMode.READ_ONLY);
let targetFile: fs.File = await fs.open(targetFilePath, fs.OpenMode.READ_WRITE);
let bufSize = 14096;
let readSize = 0;
let buf = new ArrayBuffer(bufSize);
let readOptions: ReadOptions = {
offset: readSize,
length: bufSize
};
let readLen = fs.readSync(srcFile.fd, buf, readOptions);
while (readLen > 0) {
readSize += readLen;
fs.writeSync(targetFile.fd, buf, { length: readLen });
readOptions.offset = readSize;
readLen = fs.readSync(srcFile.fd, buf, readOptions);
}
fs.closeSync(srcFile);
fs.closeSync(targetFile);
isFinally && fn && fn('200')
} catch (error) {
fn && fn('400')
console.error(`createAssetByIo :: error , msg is ${error} `);
}
}
六、附-网络图片下载
使用请求拿到的返回值就是ArrayBuffer,这个返回值继续存入沙箱下载。
// 下载网络图片转化为 ArrayBuffer
export const downloadImageWithUrl = (url: string): Promise<ArrayBuffer> => {
return new Promise(async (resolve, reject) => {
try {
http.createHttp().request(url,
(error: BusinessError, data: http.HttpResponse) => {
if (error || data.responseCode != 200) {
reject(error)
return
}
resolve(data.result as ArrayBuffer)
})
} catch (e) {
reject(e)
}
})
}
七、附-网络图片资源使用request.downloadFile下载注意事项
使用request.downloadFile()可以直接把数据写入沙箱,但是没有经够ArrayBuffer转换到沙箱,会导致呈现效果有点不一样。功能正常,如果没有这个系统弹窗就都是一致的
八、附-@pura/harmony-utils使用
harmony-utils 工具库封装好了此类相似的工作类。适合自己的才是最好的。
https://ohpm.openharmony.cn/#/cn/detail/@pura%2Fharmony-utils
九、总结
参考文章如下
如何实现PixelMap和base64的相互转换
https://developer.huawei.com/consumer/cn/doc/harmonyos-faqs-V5/faqs-image-15-V5
如查询api,则HarmonyOS搜索即可。
https://developer.huawei.com/consumer/cn/doc/search?type=all&val=getUriFromPath&versionValue=all
exampleIndex()
https://ohpm.openharmony.cn/#/cn/detail/@zyl%2Fcommonlibhar
HarmonyOS 5.0.3 Beta2 SDK,原样包含OpenHarmony SDK Ohos_sdk_public 5.0.3.131 (API Version 15 Beta2)
commonLibHar是一个公共的方法,如函数的封装
ohpm install @zyl/commonlibhar