官网中介绍了两种方法,不需要申请相册管理模块权限的方式,就可以实现图片保存到图库中。即安全控件和授权弹框的方式。看了官网中的代码,使用这两种方式,前提是都需要获取到图片的沙盒路径,安全控件还多了一种方式,通过ArrayBuffer。这些我在下面的代码中都会分享。那如何获取到沙盒路径呢?我们有的只是远程的图片链接。那么就需要用到request模块中的downloadFile方法了。用户下载远程图片,到沙盒路径中。
看代码:
下载图片:
/**
* 堆代码 duidaima.com
* 下载图片 ,获取沙盒路径(也可以获取到ArrayBuffer数据)
* @param uiContext UI上下文对象
* @param src 图片路径
* @returns 沙盒路径+图片后缀(也可以获取到ArrayBuffer数据)
*/
function handleDownloadFileGetBuffer(uiContext: UIContext, src: string): Promise<ISaveImage> {
return new Promise((resolve: (value: ISaveImage) => void, reject: () => void) => {
const context: common.UIAbilityContext = uiContext.getHostContext() as common.UIAbilityContext;
/* 获取图片的后缀名 */
const _extension = src.split('.').pop();
const extension: string = _extension ? _extension : 'jpg'; /*如果没有后缀,默认一个*/
/* 创建文件路径,相同图片也需要不同的路径,不然报错,这也是不用后端返回的文件名称的原因 */
const filePath = context.filesDir + '/' + util.generateRandomUUID(true) + '.' + extension;
console.log('filePath', filePath)
try {
request.downloadFile(context, {
url: IMG_URL + src,
filePath
}).then((downloadTask: request.DownloadTask) => {
downloadTask.on('complete', () => {
const file = fs.openSync(filePath, fs.OpenMode.READ_WRITE);
// 获取文件信息,得到图片的大小,去创建对应大小的ArrayBuffer的缓冲区
const fileInfo = fs.statSync(filePath);
const arrayBuffer = new ArrayBuffer(fileInfo.size);
fs.readSync(file.fd, arrayBuffer);
fs.closeSync(file);
const res: ISaveImage = {
extension,
sandboxUri: filePath,
arrayBuffer
}
resolve(res)
})
}).catch((err: BusinessError) => {
console.error(`Invoke downloadTask failed, code is ${err.code}, message is ${err.message}`);
});
} catch (error) {
let err: BusinessError = error as BusinessError;
console.error(`Invoke downloadFile failed, code is ${err.code}, message is ${err.message}`);
}
})
}
/* 保存图片的接口 */
interface ISaveImage {
extension: string; /*后缀名*/
sandboxUri: string;
arrayBuffer: ArrayBuffer /*资源buffer对象*/
}
其中arrayBuffer的数据获取,可以删除了。因为介绍知识点的缘故保留。通过沙盒路径的方式更加方便。
方法1:安全控件
/**
* 通过安全控件的保存控件 来保存图片
* @param uiContext ui上下文对象
* @param src 图片的路径信息
* @param result 保存控件的点击回调事件
*/
export async function handleSaveImage(uiContext: UIContext, src: string, result: SaveButtonOnClickResult) {
if (src == "") {
showToast(uiContext, "没有可保存的图片");
return;
}
const res = await handleDownloadFileGetBuffer(uiContext, src);
if (result == SaveButtonOnClickResult.SUCCESS) {
try {
const context: Context = uiContext.getHostContext() as common.UIAbilityContext;
const phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);
const photoType: photoAccessHelper.PhotoType = photoAccessHelper.PhotoType.IMAGE;
const assetChangeRequest: photoAccessHelper.MediaAssetChangeRequest =
photoAccessHelper.MediaAssetChangeRequest.createAssetRequest(context, photoType, res.extension);
// assetChangeRequest.addResource(photoAccessHelper.ResourceType.IMAGE_RESOURCE, res.arrayBuffer);
assetChangeRequest.addResource(photoAccessHelper.ResourceType.IMAGE_RESOURCE, res.sandboxUri);
await phAccessHelper.applyChanges(assetChangeRequest);
showToast(uiContext, "图片保存成功");
} catch (err) {
console.error(`create asset failed with error: ${err.code}, ${err.message}`);
}
} else {
console.error('SaveButtonOnClickResult create asset failed');
}
}
使用安全控件的方式,需要使用SaveButton组件
// 创建安全控件按钮,去保存图片。
SaveButton({ text: SaveDescription.SAVE_TO_GALLERY, buttonType: ButtonType.Capsule })
.backgroundColor("#fff")
.fontColor("#167FFF")
.fontSize(20)
.fontWeight(FontWeight.Bold)
.onClick((_event, result: SaveButtonOnClickResult) => {
handleSaveImage(this.getUIContext(), this.rentStaffQrCode.url, result);
})
方法2:授权弹框
/**
* 通过授权弹框的方式保存图片
* @param uiContext ui上下文对象
* @param src 图片的路径信息
*/
export async function handleShowAssetsCreationDialogSaveImage(uiContext: UIContext, src: string) {
if (src == "") {
showToast(uiContext, "没有可保存的图片");
return;
}
const res = await handleDownloadFileGetBuffer(uiContext, src);
try {
// 指定待保存到媒体库的位于应用沙箱的图片uri。
let srcFileUri = res.sandboxUri;
let srcFileUris: Array<string> = [
srcFileUri
];
// 指定待保存照片的创建选项,包括文件后缀和照片类型,标题和照片子类型可选。
let photoCreationConfigs: Array<photoAccessHelper.PhotoCreationConfig> = [
{
fileNameExtension: res.extension,
photoType: photoAccessHelper.PhotoType.IMAGE
}
];
// 基于弹窗授权的方式获取媒体库的目标uri。
const context: Context = uiContext.getHostContext() as common.UIAbilityContext;
const phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);
let desFileUris: Array<string> = await phAccessHelper.showAssetsCreationDialog(srcFileUris, photoCreationConfigs);
// 将来源于应用沙箱的照片内容写入媒体库的目标uri。
let desFile: fileIo.File = await fileIo.open(desFileUris[0], fileIo.OpenMode.WRITE_ONLY);
let srcFile: fileIo.File = await fileIo.open(srcFileUri, fileIo.OpenMode.READ_ONLY);
await fileIo.copyFile(srcFile.fd, desFile.fd);
fileIo.closeSync(srcFile);
fileIo.closeSync(desFile);
console.info('create asset by dialog successfully');
showToast(uiContext, "图片保存成功");
} catch (err) {
console.error(`failed to create asset by dialog successfully errCode is: ${err.code}, ${err.message}`);
}
}
授权弹框的方式,不需要用到SaveButton组件,
Text('保存至图库')
.backgroundColor("#fff")
.fontColor("#167FFF")
.fontSize(20)
.fontWeight(FontWeight.Bold)
.margin({ transform: translateY( 20, bottom: 20 })
.onClick(()=>{ handleShowAssetsCreationDialogSaveImage(this.getUIContext(), this.rentStaffQrCode.url);
})
就是有一点,看着弹出很大一个框,感觉可以预览,但实际上无法预览,这个在官网中也明确说明了,无法预览,我们就不去纠结为什么这么显示了。最近看到鸿蒙官方有活动,到2025年年底的时候,如果成功上架一个APP最高有1w元的奖励,我感觉可以试试,公司项目也不是很难,最后4个多月加油。