HarmonyOS鸿蒙Next中在不申请写入权限(ohos.permission.WRITE_IMAGEVIDEO)的情况下,把图片保存至相册

发布于 1周前 作者 ionicwang 来自 鸿蒙OS

HarmonyOS鸿蒙Next中在不申请写入权限(ohos.permission.WRITE_IMAGEVIDEO)的情况下,把图片保存至相册 在不申请写入权限(ohos.permission.WRITE_IMAGEVIDEO)的情况下,把图片保存至相册
麻烦提供一下Demo,谢谢

3 回复

参考demo:

//Constants.ets

export const BASE64_IMAGE_STR = 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QBYRXhpZgAATU0AKgAAAAgAAgESAAMAAAABAAEAAIdpAAQAAAABAAAAJgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAACPKADAAQAAAABAAADWQAAAAD/7QA4UGhvdG9zaG9wIDMuMA'

因文件太大,请自行准备......

```javascript
//SaveImageUtil.ets

/**
 * 保存图片工具类
 */

import { BusinessError, request } from '[@kit](/user/kit).BasicServicesKit'
import { common } from '[@kit](/user/kit).AbilityKit'
import { buffer } from '[@kit](/user/kit).ArkTS'
import { fileIo as fs, fileUri } from '[@kit](/user/kit).CoreFileKit'
import { photoAccessHelper } from '[@kit](/user/kit).MediaLibraryKit'
import { FileUtil } from './FileUtil'

/**
 * 保存文件到应用沙箱
 * [@param](/user/param) image 图片url或base64
 * [@param](/user/param) dirPath 保存文件夹路径
 * [@param](/user/param) fileName 保存文件名
 */
async function saveImageToApplication(image: string, dirPath: string, fileName: string): Promise<SaveImageResultVo> {
  return new Promise(async (resolve: Function, reject: Function) => {
    if (!fs.accessSync(dirPath)) {
      fs.mkdirSync(dirPath)
    }
    if (image.startsWith('http')) {
      // 如果是网络图片,则下载图片
      try {
        request
          .downloadFile(this.context, { url: image, filePath: `${dirPath}/${fileName}` })
          .then((downloadTask) => {
            console.debug(`image => 保存图片到应用沙盒`)
            downloadTask
              .on('complete', () => {
                // 图片下载完成
                let vo = new SaveImageResultVo
                vo.errorCode = 0
                resolve(vo)
              })
            downloadTask
              .on('fail', (err: number) => {
                let vo = new SaveImageResultVo
                vo.errorCode = err
                resolve(vo)
              })
          })
          .catch((error: BusinessError) => {
            reject(error)
          })
      } catch (err) {
        reject(err)
      }
    } else {
      // base64图片
      let content = image
      if (image.startsWith('data:')) {
        // 如果是data:,则截取,后的数据
        content = image.split(',')[1]
      }
      try {
        const decodeBuff = buffer.from(content, 'base64').buffer
        FileUtil.writeBufferToFile(dirPath, fileName, decodeBuff)
        let vo = new SaveImageResultVo
        vo.errorCode = 0
        resolve(vo)
      } catch (err) {
        reject(err)
      }
    }
  })
}

/**
 * 保存图片到相册
 * [@param](/user/param) path 图片路径
 * [@param](/user/param) title 标题
 * [@param](/user/param) extension 文件扩展名
 * [@returns](/user/returns)
 */
async function saveImageToAlbum(path: string, title?: string, extension?: string): Promise<boolean> {
  return new Promise(async (resolve: Function, reject: Function) => {
    try {
      let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(this.context)
      let srcFileUri: Array<string> = [fileUri.getUriFromPath(path)]
      let photoCreationConfigs: Array<photoAccessHelper.PhotoCreationConfig> = [
        {
          title: '保存图片',
          fileNameExtension: extension ? extension : 'jpg',
          photoType: photoAccessHelper.PhotoType.IMAGE,
          subtype: photoAccessHelper.PhotoSubtype.DEFAULT
        }
      ]
      let desFileUris: Array<string> = await phAccessHelper.showAssetsCreationDialog(srcFileUri, photoCreationConfigs)
      if (desFileUris.length > 0) {
        for (let index = 0; index < desFileUris.length; index++) {
          FileUtil.copyFileContentTo(srcFileUri[index], desFileUris[index])
        }
        resolve(true)
      } else {
        resolve(false)
      }
    } catch (err) {
      reject(err)
    }
  })
}
export class SaveImageResultVo {
  errorCode: number = 0
}
export default new SaveImageUtil()
//FileUtil.ets

import fs, { ReadOptions, WriteOptions } from '[@ohos](/user/ohos).file.fs'
import { util } from '[@kit](/user/kit).ArkTS'
import { common } from '[@kit](/user/kit).AbilityKit'

class FileUtil {
  private context = getContext(this) as common.UIAbilityContext

  /**
   * 将内容写入文件
   * [@param](/user/param) dirPath 文件及路径
   * [@param](/user/param) fileName 文件名称
   * [@param](/user/param) content 内容
   */
  writeContentToFile(dirPath: string, fileName: string, content: string) {
    if (!fs.accessSync(dirPath)) {
      // 如果文件夹不存在,则先创建文件夹
      fs.mkdirSync(dirPath)
    }
    let file = fs.openSync(`${dirPath}/${fileName}`, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE)
    fs.writeSync(file.fd, content)
    fs.closeSync(file)
  }

  /**
   * 保存文件
   * [@param](/user/param) dirPath
   * [@param](/user/param) fileName
   * [@param](/user/param) buf
   */
  writeBufferToFile(dirPath: string, fileName: string, buf: ArrayBuffer) {
    if (!fs.accessSync(dirPath)) {
      // 如果文件夹不存在,则先创建文件夹
      fs.mkdirSync(dirPath)
    }
    let file = fs.openSync(`${dirPath}/${fileName}`, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE)
    fs.writeSync(file.fd, buf)
    fs.closeSync(file)
  }

  /**
   * 读取文件内容
   * [@param](/user/param) filePath 文件路径
   * [@returns](/user/returns) 文件内容
   */
  readContentFromFile(filePath: string): string {
    let result = ''
    if (fs.accessSync(filePath)) {
      let file = fs.openSync(filePath, fs.OpenMode.READ_ONLY)
      let bufSize = 4096
      let readSize = 0
      let buf = new ArrayBuffer(bufSize)
      let readOptions: ReadOptions = {
        offset: readSize,
        length: bufSize
      }
      let readLen = fs.readSync(file.fd, buf, readOptions)
      result = util.TextDecoder.create('utf-8').decodeWithStream(new Uint8Array(buf))
      while (readLen > 0) {
        readSize += readLen
        readOptions.offset = readSize
        readLen = fs.readSync(file.fd, buf, readOptions)
        result += util.TextDecoder.create('utf-8').decodeWithStream(new Uint8Array(buf))
      }
      fs.closeSync(file)
    }
    return result
  }

  /**
   * 复制文件内容到目标文件
   * [@param](/user/param) srcFilePath 源文件路径
   * [@param](/user/param) destFilePath 目标文件路径
   */
  copyFileContentTo(srcFilePath: string, destFilePath: string) {
    let srcFile = fs.openSync(srcFilePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE)
    let destFile = fs.openSync(destFilePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE)
    // 读取源文件内容并写入至目的文件
    let bufSize = 4096;
    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;
      let writeOptions: WriteOptions = {
        length: readLen
      };
      fs.writeSync(destFile.fd, buf, writeOptions);
      readOptions.offset = readSize;
      readLen = fs.readSync(srcFile.fd, buf, readOptions);
    }
    // 关闭文件
    fs.closeSync(srcFile);
    fs.closeSync(destFile);
  }

  /**
   * 删除文件
   * [@param](/user/param) filePath 文件路径
   */
  deleteFile(filePath: string) {
    if (fs.accessSync(filePath)) {
      fs.rmdirSync(filePath)
    }
  }

  /**
   * 将Raw中的文件复制到应用沙盒中
   * [@param](/user/param) rawFilePath Raw中文件路径
   * [@param](/user/param) destDirPath 目标文件夹路径
   * [@param](/user/param) fileName 文件名
   */
  copyRawFileToApplicationStorage(rawFilePath: string, destDirPath: string, fileName: string) {
    if (!fs.accessSync(destDirPath)) {
      // 如果文件夹不存在,则先创建文件夹
      fs.mkdirSync(destDirPath)
    }
    let srcFile = this.context.resourceManager.getRawFdSync(rawFilePath)
    let dest = fs.openSync(`${destDirPath}/${fileName}`, fs.OpenMode.CREATE | fs.OpenMode.READ_WRITE)
    let bufSize = 4096
    let buf = new ArrayBuffer(bufSize)
    let off = 0, len = 0, readLength = 0
    while (len = fs.readSync(srcFile.fd, buf, { offset: srcFile.offset + off, length: bufSize })) {
      readLength += len
      fs.writeSync(dest.fd, buf, { offset: off, length: len })
      off = off + len
      if ((srcFile.length - readLength) < bufSize) {
        bufSize = srcFile.length - readLength
      }
    }
    fs.close(dest.fd)
  }
}

export default new FileUtil()
//Index.ets

import { SaveImageResultVo, SaveImageUtil } from './SaveImageUtil'
import { common } from '[@kit](/user/kit).AbilityKit';
import { BusinessError, request } from '[@kit](/user/kit).BasicServicesKit';
import { BASE64_IMAGE_STR } from './Constants';
import { fileIo } from '[@kit](/user/kit).CoreFileKit';

[@Entry](/user/Entry)
[@Component](/user/Component)
struct Index {
  private context = getContext(this) as common.UIAbilityContext

  build() {
    Column() {
      Button('保存网络图片到应用沙盒')
        .width('100%')
        .height(40)
        .fontSize(16)
        .fontWeight(FontWeight.Bold)
        .fontColor(Color.White)
        .onClick(async () => {
          console.debug(`image => 保存网络图片到应用沙盒`)
          let dirPath = this.context.cacheDir
          let fileName = 'temp2.jpg'
          if (fileIo.accessSync(`${dirPath}/${fileName}`)) {
            fileIo.rmdirSync(`${dirPath}/${fileName}`)
          }
          try {
            let result: SaveImageResultVo = await SaveImageUtil.saveImageToApplication(
              // 'https://wx3.sinaimg.cn/mw690/006N8b3Kgy1hqe7d2hfboj31jk267h17.jpg'
              `http://gips0.baidu.com/it/u=3602773692,1512483864&fm=3028&app=3028&f=JPEG&fmt=auto?w=960&h=1280`
              , dirPath, fileName
            )
            if (0 == result.errorCode) {
              // 图片保存成功
              console.debug(`image => 保存图片到应用沙盒成功`)
              SaveImageUtil
                .saveImageToAlbum(`${dirPath}/${fileName}`)
                .then(result => {
                  if (result) {
                    console.debug(`image => 保存图片到相册成功`)
                  } else {
                    console.debug(`image => 用户拒绝授权`)
                  }
                })
                .catch(error => {
                  console.error('image => 保存图片到相册异常:')
                })
            }
          } catch (err) {
            console.error(err, `image => 保存图片到应用沙盒异常:`)
          }
        })

      Button('保存BASE64图片到应用沙盒')
        .width('100%')
        .height(40)
        .fontSize(16)
        .fontWeight(FontWeight.Bold)
        .fontColor(Color.White)
        .margin({ top: 15 })
        .onClick(() => {
          console.debug(`image => 保存网络图片到应用沙盒`)
          let dirPath = this.context.cacheDir
          let fileName = 'temp2.jpg'
          if (fileIo.accessSync(`${dirPath}/${fileName}`)) {
            fileIo.rmdirSync(`${dirPath}/${fileName}`)
          }
          SaveImageUtil
            .saveImageToApplication(BASE64_IMAGE_STR, dirPath, fileName)
            .then(data => {
              if (0 == data.errorCode) {
                console.debug(`image => 保存图片到应用沙盒成功`)
              } else {
                console.debug(`image => 保存图片到应用沙盒失败:${data.errorCode}`)
              }
            })
            .catch(error => {
              console.error(`image => 保存图片到应用沙盒异常:`)
            })
        })
    }
    .padding(15)
    .height('100%')
    .width('100%')
  }
}

更多关于HarmonyOS鸿蒙Next中在不申请写入权限(ohos.permission.WRITE_IMAGEVIDEO)的情况下,把图片保存至相册的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS鸿蒙Next中,若不申请ohos.permission.WRITE_IMAGEVIDEO权限,无法直接将图片保存至系统相册。系统相册的写入操作需要该权限以确保数据安全和隐私保护。若未申请权限,尝试保存图片将导致权限拒绝异常。开发者需在应用中明确请求该权限,并在用户授权后方可进行写入操作。

在HarmonyOS鸿蒙Next中,若未申请ohos.permission.WRITE_IMAGEVIDEO权限,无法直接将图片保存至相册。系统出于安全考虑,限制了对相册的写入操作。建议在应用中使用ohos.permission.WRITE_IMAGEVIDEO权限声明,并在运行时动态申请权限,以确保合规性和用户隐私。若用户拒绝权限,可引导用户手动保存图片至其他目录。

回到顶部
AI 助手
你好,我是IT营的 AI 助手
您可以尝试点击下方的快捷入口开启体验!