HarmonyOS NEXT Practical: Save Network Images

Objective: Display network images, download them by clicking the save button, and save them locally. Prerequisite: ohos.permission.INTERNET Permission application required Implementation idea: Display images through Image. Obtain operational permissions through SaveButton. Download images through request.downloadFile. Copy images locally through fileIo. Specific implementation import { http } from '@kit.NetworkKit'; import { image } from '@kit.ImageKit'; import { BusinessError, request } from '@kit.BasicServicesKit'; import { photoAccessHelper } from '@kit.MediaLibraryKit'; import { promptAction } from '@kit.ArkUI'; import { fileIo, fileIo as fs, fileUri } from '@kit.CoreFileKit'; import { common } from '@kit.AbilityKit'; @Entry @Component struct SaveImageDemoPage { imgUrl:string = 'https://pic1.zhimg.com/70/v2-88fd131a2081f6880036682526e40f4b_1440w.avis?source=172ae18b&biz_tag=Post' @State pixelMap: PixelMap | undefined = undefined; loadImageWithUrl(url: string) { let responseCode = http.ResponseCode; let OutData: http.HttpResponse; let imagePackerApi = image.createImagePacker(); let packOpts: image.PackingOption = { format: 'image/jpeg', quality: 98 }; // 确保网络正常 http.createHttp().request(url, { method: http.RequestMethod.GET, connectTimeout: 60000, readTimeout: 60000 }, async (error: BusinessError, data: http.HttpResponse) => { if (error) { console.error(`http request failed with. Code: ${error.code}, message: ${error.message}`); } else { OutData = data; let code: http.ResponseCode | number = OutData.responseCode; if (responseCode.OK === code) { let imageData: ArrayBuffer = OutData.result as ArrayBuffer; let imageSource: image.ImageSource = image.createImageSource(imageData); class tmp { height: number = 100 width: number = 100 }; let options: Record = { 'alphaType': 0, // 透明度 'editable': false, // 是否可编辑 'pixelFormat': 3, // 像素格式 'scaleMode': 1, // 缩略值 'size': { height: 100, width: 100 } }; // 创建图片大小 imageSource.createPixelMap(options).then((pixelMap: PixelMap) => { this.pixelMap = pixelMap; this.pixelMap.getImageInfo().then((info: image.ImageInfo) => { console.info('info.width = ' + info.size.width); }).catch((err: BusinessError) => { console.error('Failed ' + err); }) imagePackerApi.packing(pixelMap, packOpts).then(async (buffer: ArrayBuffer) => { try { const context = getContext(this); let helper = photoAccessHelper.getPhotoAccessHelper(context); let uri = await helper.createAsset(photoAccessHelper.PhotoType.IMAGE, 'png'); let file = await fs.open(uri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE); // 写入文件 await fs.write(file.fd, buffer); promptAction.showToast({ message: '已保存至相册!' }); // 关闭文件 await fs.close(file.fd); } catch (error) { console.error('error is ' + JSON.stringify(error)); } }).catch((error: BusinessError) => { console.error('Failed to pack the image. And the error is: ' + error); }) pixelMap.release(); }) } } } ) } build() { Row() { Column({ space: 10 }) { Image(this.imgUrl) .width('80%') SaveButton().onClick(async (event: ClickEvent, result: SaveButtonOnClickResult) => { if (result === SaveButtonOnClickResult.SUCCESS) { this.loadImageWithUrl(this.imgUrl); } else { promptAction.showToast({ message: '设置权限失败!' }); } }) } .width('100%') } .height('100%') .backgroundColor(0xF1F3F5) } /** * 保存图片 * @param url */ async saveImage(imageUrl: string) { try { let fileNameExtension = this.getFileNameExtension(imageUrl) fileNameExtension = (fileNameExtension == '' || fileNameExtension == 'jfif') ? 'jpg' : fileNameExtension let context = getContext(this) as common.UIAbilityContext; let dirPath = context.filesDir + '/article_images_preview' let fileName = new Date().getTime().toString() if (!fileIo.accessSync(dirPath)) { fileIo.mkdirSync(dirPath) } //下载网络图片,并保存到沙箱 request.downloadFile(context, { url: imageUrl, filePath: `${dirPath}/${fileName}.${fileNameExtension}` }) .then((downloadTask) => { downloadTask.on('complete', async () => { try { // 位于应用沙箱的图片uri let srcFileUri = `${dirPath}/${fileName}.${fileNameExtension}`

Mar 26, 2025 - 09:46
 0
HarmonyOS NEXT Practical: Save Network Images

Objective: Display network images, download them by clicking the save button, and save them locally.

Prerequisite: ohos.permission.INTERNET Permission application required

Implementation idea:

  1. Display images through Image.
  2. Obtain operational permissions through SaveButton.
  3. Download images through request.downloadFile.
  4. Copy images locally through fileIo.

Specific implementation

import { http } from '@kit.NetworkKit';
import { image } from '@kit.ImageKit';
import { BusinessError, request } from '@kit.BasicServicesKit';
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { promptAction } from '@kit.ArkUI';
import { fileIo, fileIo as fs, fileUri } from '@kit.CoreFileKit';
import { common } from '@kit.AbilityKit';

@Entry
@Component
struct SaveImageDemoPage {
  imgUrl:string = 'https://pic1.zhimg.com/70/v2-88fd131a2081f6880036682526e40f4b_1440w.avis?source=172ae18b&biz_tag=Post'
  @State pixelMap: PixelMap | undefined = undefined;

  loadImageWithUrl(url: string) {
    let responseCode = http.ResponseCode;
    let OutData: http.HttpResponse;
    let imagePackerApi = image.createImagePacker();
    let packOpts: image.PackingOption = { format: 'image/jpeg', quality: 98 };
    // 确保网络正常
    http.createHttp().request(url, {
      method: http.RequestMethod.GET,
      connectTimeout: 60000,
      readTimeout: 60000
    },
      async (error: BusinessError, data: http.HttpResponse) => {
        if (error) {
          console.error(`http request failed with. Code: ${error.code}, message: ${error.message}`);
        } else {
          OutData = data;
          let code: http.ResponseCode | number = OutData.responseCode;
          if (responseCode.OK === code) {
            let imageData: ArrayBuffer = OutData.result as ArrayBuffer;
            let imageSource: image.ImageSource = image.createImageSource(imageData);

            class tmp {
              height: number = 100
              width: number = 100
            };

            let options: Record = {
              'alphaType': 0, // 透明度
              'editable': false, // 是否可编辑
              'pixelFormat': 3, // 像素格式
              'scaleMode': 1, // 缩略值
              'size': { height: 100, width: 100 }
            }; // 创建图片大小
            imageSource.createPixelMap(options).then((pixelMap: PixelMap) => {
              this.pixelMap = pixelMap;
              this.pixelMap.getImageInfo().then((info: image.ImageInfo) => {
                console.info('info.width = ' + info.size.width);
              }).catch((err: BusinessError) => {
                console.error('Failed ' + err);
              })
              imagePackerApi.packing(pixelMap, packOpts).then(async (buffer: ArrayBuffer) => {
                try {
                  const context = getContext(this);
                  let helper = photoAccessHelper.getPhotoAccessHelper(context);
                  let uri = await helper.createAsset(photoAccessHelper.PhotoType.IMAGE, 'png');
                  let file = await fs.open(uri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
                  // 写入文件
                  await fs.write(file.fd, buffer);
                  promptAction.showToast({ message: '已保存至相册!' });
                  // 关闭文件
                  await fs.close(file.fd);
                } catch (error) {
                  console.error('error is ' + JSON.stringify(error));
                }
              }).catch((error: BusinessError) => {
                console.error('Failed to pack the image. And the error is: ' + error);
              })
              pixelMap.release();
            })
          }
        }
      }
    )
  }

  build() {
    Row() {
      Column({ space: 10 }) {
        Image(this.imgUrl)
          .width('80%')

        SaveButton().onClick(async (event: ClickEvent, result: SaveButtonOnClickResult) => {
          if (result === SaveButtonOnClickResult.SUCCESS) {
            this.loadImageWithUrl(this.imgUrl);
          } else {
            promptAction.showToast({ message: '设置权限失败!' });
          }
        })
      }
      .width('100%')
    }
    .height('100%')
    .backgroundColor(0xF1F3F5)
  }

  /**
   * 保存图片
   * @param url
   */
  async saveImage(imageUrl: string) {
    try {
      let fileNameExtension = this.getFileNameExtension(imageUrl)
      fileNameExtension = (fileNameExtension == '' || fileNameExtension == 'jfif') ? 'jpg' : fileNameExtension
      let context = getContext(this) as common.UIAbilityContext;
      let dirPath = context.filesDir + '/article_images_preview'
      let fileName = new Date().getTime().toString()
      if (!fileIo.accessSync(dirPath)) {
        fileIo.mkdirSync(dirPath)
      }
      //下载网络图片,并保存到沙箱
      request.downloadFile(context, { url: imageUrl, filePath: `${dirPath}/${fileName}.${fileNameExtension}` })
        .then((downloadTask) => {
          downloadTask.on('complete', async () => {
            try {
              // 位于应用沙箱的图片uri
              let srcFileUri = `${dirPath}/${fileName}.${fileNameExtension}`
              let srcFileUris: Array = [fileUri.getUriFromPath(srcFileUri)];
              let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);
              // 指定待保存照片的创建选项,包括文件后缀和照片类型,标题和照片子类型可选
              let photoCreationConfigs: Array = [
                {
                  title: fileName, // 可选
                  fileNameExtension: fileNameExtension,
                  photoType: photoAccessHelper.PhotoType.IMAGE,
                  subtype: photoAccessHelper.PhotoSubtype.DEFAULT, // 可选
                }
              ];
              // 基于弹窗授权的方式获取媒体库的目标uri
              let desFileUris: Array =
                await phAccessHelper.showAssetsCreationDialog(srcFileUris, photoCreationConfigs);
              if (desFileUris.length > 0) {
                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);
                promptAction.showToast({ message: '已保存!' });
              } else {
                promptAction.showToast({ message: '保存失败!' });
              }
              fs.rmdir(dirPath);
            } catch (error) {
              promptAction.showToast({ message: '保存失败!' });
            }
          })
          downloadTask.on('fail', (err: number) => {
            //下载失败
            promptAction.showToast({ message: '保存失败!' });
          })
        })
        .catch((err: BusinessError) => {
          promptAction.showToast({ message: '保存失败!' });
        })
    } catch (err) {
      promptAction.showToast({ message: '保存失败!' });
    }
  }

  /**
   * 获取文件拓展名
   * @param url
   * @returns 文件扩展名,没有则返回空字符串
   */
  getFileNameExtension(url: string): string {
    // 查找最后一个点(.)的位置
    const lastIndex = url.lastIndexOf('.');
    let fileNameExtension = ''
    if (lastIndex === -1 || lastIndex === url.length - 1) {
      fileNameExtension = ''
    } else {
      // 提取从最后一个点到字符串末尾的子字符串
      let subStr = url.substring(lastIndex);
      // 使用正则表达式匹配文件后缀名
      const extensionMatch = subStr.match(/\.([a-zA-Z0-9]+)(?:[\?#]|$)/);
      // 如果匹配到后缀名,则返回(去掉点号)
      fileNameExtension = extensionMatch ? extensionMatch[1].toLowerCase() : '';
    }
    return fileNameExtension
  }
}