import fileIo from '@ohos.fileio';
import photoAccessHelper from '@ohos.file.photoAccessHelper';
import fileUri from '@ohos.file.fileuri';
import promptAction from '@ohos.promptAction';
import image from '@ohos.multimedia.image';
import componentSnapshot from '@ohos.componentSnapshot';
import abilityAccessCtrl, { Permissions } from '@ohos.abilityAccessCtrl';
import common from '@ohos.app.ability.common';

@Component
export struct SaveImageComponent {
  private controller: CustomDialogController;
  
  // 需要申请的权限
  private readonly requiredPermissions: Permissions[] = [
    'ohos.permission.READ_IMAGEVIDEO', 
    'ohos.permission.WRITE_IMAGEVIDEO'
  ];

  async saveImage() {
    try {
      // 1. 申请必要权限
      await this.requestPermissions();
      
      // 2. 截图并转换为二进制流
      const arrayBuffer = await this.captureComponent();
      
      // 3. 保存图片到应用沙箱
      const imagePath = await this.saveToSandbox(arrayBuffer);
      
      // 4. 保存到系统相册
      await this.saveToGallery(imagePath);
      
      // 5. 显示成功提示
      promptAction.showToast({ 
        message: '图片保存成功', 
        duration: 2000 
      });
      
    } catch (error) {
      console.error('图片保存失败:', JSON.stringify(error));
      promptAction.showToast({ 
        message: `保存失败: ${error.message || '未知错误'}`, 
        duration: 3000 
      });
    } finally {
      // 无论成功失败都关闭弹窗
      this.controller.close();
    }
  }

  // 申请必要权限
  private async requestPermissions(): Promise<void> {
    try {
      const context = getContext(this) as common.UIAbilityContext;
      const atManager = abilityAccessCtrl.createAtManager();
      
      const grantStatus = await atManager.requestPermissionsFromUser(
        context, 
        this.requiredPermissions
      );
      
      // 检查所有权限是否都授予
      const allGranted = this.requiredPermissions.every(permission => {
        return grantStatus.authResults[permission] === 0;
      });
      
      if (!allGranted) {
        throw new Error('所需权限未完全授予');
      }
    } catch (error) {
      console.error('权限申请失败:', JSON.stringify(error));
      throw new Error('权限不足,无法保存图片');
    }
  }

  // 截图组件并转换为二进制流
  private async captureComponent(): Promise<ArrayBuffer> {
    try {
      const imgMsg = await componentSnapshot.get('share');
      const imagePacker = image.createImagePacker();
      return await imagePacker.packToData(imgMsg, {
        format: 'image/jpeg', 
        quality: 98
      });
    } catch (error) {
      console.error('截图失败:', JSON.stringify(error));
      throw new Error('截图生成失败');
    }
  }

  // 保存图片到应用沙箱
  private async saveToSandbox(arrayBuffer: ArrayBuffer): Promise<string> {
    let fileFd: number | null = null;
    
    try {
      const context = getContext(this);
      const timestamp = new Date().getTime();
      const imagePath = `${context.cacheDir}/screenshot_${timestamp}.jpeg`;
      
      // 创建文件并获取文件描述符
      fileFd = fileIo.openSync(imagePath, 
        fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE
      );
      
      // 写入数据
      const bytesWritten = fileIo.writeSync(fileFd, arrayBuffer);
      
      if (bytesWritten <= 0) {
        throw new Error('图片写入失败');
      }
      
      return imagePath;
    } catch (error) {
      console.error('沙箱保存失败:', JSON.stringify(error));
      throw new Error('图片保存到临时位置失败');
    } finally {
      // 确保文件描述符关闭
      if (fileFd !== null) {
        fileIo.closeSync(fileFd);
      }
    }
  }

  // 保存到系统相册
  private async saveToGallery(imagePath: string): Promise<void> {
    try {
      const context = getContext(this);
      const uri = fileUri.getUriFromPath(imagePath);
      
      // 创建图片保存请求
      const photoRequest = 
        photoAccessHelper.MediaAssetChangeRequest.createImageAssetRequest(context, uri);
      
      if (!photoRequest) {
        throw new Error('创建图片保存请求失败');
      }
      
      const photoHelper = photoAccessHelper.getPhotoAccessHelper(context);
      
      // 应用更改(保存图片)
      await photoHelper.applyChanges([photoRequest]);
      
    } catch (error) {
      console.error('保存到相册失败:', JSON.stringify(error));
      throw new Error('图片保存到相册失败');
    }
  }
}

添加权限申请​​:在 module.json5 中添加:

"requestPermissions": [
  {
    "name": "ohos.permission.READ_IMAGEVIDEO"
  },
  {
    "name": "ohos.permission.WRITE_IMAGEVIDEO"
  }
]

 在UI中使用​​:

@Entry
@Component
struct MyComponent {
  private saveDialogController: CustomDialogController | null = null;
  
  build() {
    Button('保存图片')
      .onClick(() => {
        this.saveDialogController = new CustomDialogController({
          builder: SaveImageComponent({ controller: this.saveDialogController }),
        });
        this.saveDialogController.open();
      })
  }
}

简写(权限需要自己配置)

controller: CustomDialogController
async saveImage(){
   //1.截图组件变成二进制流
    //1.1 组件信息
    //除了id要改其他的代码都可以原封不动的复用
    const imgMsg = await componentSnapshot.get('share')
    //1.2 创建imagePacker对象实例
    const imagePacker = image.createImagePacker()
    //1.3 将组件截图变成二进制流
    const arrayBuffer = await imagePacker.packToData(imgMsg,{format:'image/jpeg', quality: 98 })
    //2.将图片存储到沙箱
    //2.1获取context对象
    const ctx = getContext(this)
    //2.2生成一个唯一一个的图片路径
    const imagePath = ctx.cacheDir + '/' + Date.now() +'.jpeg'
    //2.3创建文件
    // 打开一个未创建的图片,让他具备创建和读写能力
    // 参数1: imagePath 文件的路径
    // 参数2: 因为还没有文件  fileIo.OpenMode.CREATE 所以先创建 fileIo.OpenMode.READ_WRITE 并设置读写能力
    const file = fileIo.openSync(imagePath,fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE)
    //2.4二进制写入沙箱
    fileIo.writeSync(file.fd,arrayBuffer)
    fileIo.closeSync(file.fd)
    //3.从沙箱目录拿到图片保存到相册
    //3.1获取uri地址
    const uri = fileUri.getUriFromPath(imagePath)
    //3.2获取资源文件请求
    const photoRequest = photoAccessHelper.MediaAssetChangeRequest.createImageAssetRequest(ctx, uri)
    //3.3创建获取相册实例
    const photoHelper = photoAccessHelper.getPhotoAccessHelper(ctx)
    //3.4保存到相册
    photoHelper.applyChanges(photoRequest)
    //3.5弹窗提示
    promptAction.showToast({ message: '保存成功' })
    //4.关闭弹框
    this.controller.close()
  }

用以前的demo试下,ok没有问题~~

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐