目录

文档扫描后保存图片预览页面空白

问题现象

文档扫描控件DocumentScanner扫描出图片后,使用相册管理模块弹窗showAssetsCreationDialog保存图片,预览页面空白。

背景知识

问题定位

分析结论

当使用文档扫描控件DocumentScanner完成回调后得到URI列表,再调用接口showAssetsCreationDialog,由于showAssetsCreationDialog的入参srcFileUris需要使用fileUri.getUriFromPath获取的沙箱路径URI或者媒体文件URI,才可以正常预览,但实际入参的srcFileUris是临时生成的,导致保存图片预览页面空白。

修改建议

使用文档扫描控件DocumentScanner完成回调后,将图片保存至沙箱目录下,后调用接口showAssetsCreationDialog保存图片,可以正常显示并保存图片至图库。

  1. 根据业务场景将扫描得到的图片保存至沙箱目录下。
    saveListPicture(uris: string[]) {
      // 将图片保存至沙箱目录下
      let srcFile = fileIo.openSync(uris[0], fileIo.OpenMode.READ_ONLY);
      const destFileUri = fileUri.getUriFromPath(`${this.context.cacheDir}/${util.generateRandomUUID()}.jpg`);
      let destFile: fileIo.File = fileIo.openSync(destFileUri, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE);
      fileIo.copyFileSync(srcFile.fd, destFile.fd);
      fileIo.closeSync(srcFile);
      fileIo.closeSync(destFile);
      hilog.info(0x0001, TAG, `文件路径:${destFileUri}`);
    
      this.saveImageToAsset(destFileUri, 'jpg').then(() => {
        promptAction.openToast({ message: '保存成功' });
      }).catch(() => {
        promptAction.openToast({ message: '保存失败' });
      });
    }
  2. 使用fileUri.getUriFromPath获取保存到沙箱目录的文件URI作为参数,调用接口showAssetsCreationDialog保存图片。
    async saveImageToAsset(uri: string, nameExtension: string): Promise<void> {
      hilog.info(0x0001, TAG, `ShowAssetsCreationDialogDemo:${uri}`);
      try {
        let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(this.context);
        // 获取需要保存到媒体库的位于应用沙箱的图片/视频uri
        let srcFileUris: Array<string> = [uri];
        let photoCreationConfigs: Array<photoAccessHelper.PhotoCreationConfig> = [{
          title: 'test',
          // 可选
          fileNameExtension: nameExtension,
          photoType: photoAccessHelper.PhotoType.IMAGE,
          // 可选,支持:普通图片、动态图片
          subtype: photoAccessHelper.PhotoSubtype.DEFAULT,
        }];
        let desFileUris: Array<string> = await phAccessHelper.showAssetsCreationDialog(srcFileUris, photoCreationConfigs);
        hilog.info(0x0001, TAG, `showAssetsCreationDialog success, data is:${desFileUris}`);
        if (desFileUris.length == 0) {
          // 用户拒绝保存
          throw (new Error('用户拒绝保存'));
        }
        this.createAssetByIo(uri, desFileUris[0]);
        return Promise.resolve();
      } catch (err) {
        hilog.error(0x0001, TAG, `showAssetsCreationDialog failed, errCode is:${err.code}` + `errMsg is:${err.message}`);
        return Promise.reject(err);
      }
    }
    
    createAssetByIo(sourceFilePath: string, targetFilePath: string) {
      try {
        hilog.info(0x0001, TAG, `文件路径:${this.context.filesDir}`);
        let srcFile: fileIo.File = fileIo.openSync(sourceFilePath, fileIo.OpenMode.READ_ONLY);
        let targetFile: fileIo.File = fileIo.openSync(targetFilePath, fileIo.OpenMode.READ_WRITE);
        fileIo.copyFileSync(srcFile.fd, targetFile.fd);
        fileIo.closeSync(srcFile);
        fileIo.closeSync(targetFile);
      } catch (error) {
        hilog.error(0x0001, TAG, `showAssetsCreationDialog failed, msg is:${error}`);
      }
    }

完整代码如下:

import {
  DocType,
  DocumentScanner,
  DocumentScannerConfig,
  SaveOption,
  FilterId,
  ShootingMode
} from '@kit.VisionKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { fileUri } from '@kit.CoreFileKit';
import { util } from '@kit.ArkTS';
import { fileIo } from '@kit.CoreFileKit';
import { promptAction } from '@kit.ArkUI';
import { common } from '@kit.AbilityKit';

const TAG: string = 'DocDemoPage';

@Entry
@Component
export struct DocDemoPage {
  @State docImageUris: string[] = [];
  private docScanConfig = new DocumentScannerConfig();
  context: common.UIAbilityContext = this.getUIContext().getHostContext() as common.UIAbilityContext;

  aboutToAppear() {
    this.docScanConfig.supportType = [DocType.DOC, DocType.SHEET];
    this.docScanConfig.isGallerySupported = true;
    this.docScanConfig.editTabs = [];
    this.docScanConfig.maxShotCount = 3;
    this.docScanConfig.defaultFilterId = FilterId.ORIGINAL;
    this.docScanConfig.defaultShootingMode = ShootingMode.MANUAL;
    this.docScanConfig.isShareable = true;
    this.docScanConfig.originalUris = [];
  }

  build() {
    Column() {
      Stack({ alignContent: Alignment.Top }) {
        // 展示文档扫描结果
        List() {
          ForEach(this.docImageUris, (uri: string) => {
            ListItem() {
              Image(uri)
                .objectFit(ImageFit.Contain)
                .width(100)
                .height(100);
            };
          });
        }
        .listDirection(Axis.Vertical)
        .alignListItem(ListItemAlign.Center)
        .margin({
          top: 50
        })
        .width('80%')
        .height('80%');

        // 文档扫描
        DocumentScanner({
          scannerConfig: this.docScanConfig,
          onResult: (code: number, saveType: SaveOption, uris: string[]) => {
            hilog.info(0x0001, TAG, `result code: ${code}, save: ${saveType}`);
            if (code === -1) {
              promptAction.openToast({ message: 'failed' });
            } else {
              if (uris.length > 0) {
                this.saveListPicture(uris);
              }
            }
            uris.forEach(uriString => {
              hilog.info(0x0001, TAG, `uri: ${uriString}`);
            });
            this.docImageUris = uris;
          }
        })
          .size({ width: '100%', height: '100%' });
      }
      .width('100%')
      .height('100%');
    }
    .width('100%')
    .height('100%');
  }

  saveListPicture(uris: string[]) {
    // 将图片保存至沙箱目录下
    let srcFile = fileIo.openSync(uris[0], fileIo.OpenMode.READ_ONLY);
    const destFileUri = fileUri.getUriFromPath(`${this.context.cacheDir}/${util.generateRandomUUID()}.jpg`);
    let destFile: fileIo.File = fileIo.openSync(destFileUri, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE);
    fileIo.copyFileSync(srcFile.fd, destFile.fd);
    fileIo.closeSync(srcFile);
    fileIo.closeSync(destFile);
    hilog.info(0x0001, TAG, `文件路径:${destFileUri}`);

    this.saveImageToAsset(destFileUri, 'jpg').then(() => {
      promptAction.openToast({ message: '保存成功' });
    }).catch(() => {
      promptAction.openToast({ message: '保存失败' });
    });
  }

  async saveImageToAsset(uri: string, nameExtension: string): Promise<void> {
    hilog.info(0x0001, TAG, `ShowAssetsCreationDialogDemo:${uri}`);
    try {
      let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(this.context);
      // 获取需要保存到媒体库的位于应用沙箱的图片/视频uri
      let srcFileUris: Array<string> = [uri];
      let photoCreationConfigs: Array<photoAccessHelper.PhotoCreationConfig> = [{
        title: 'test',
        // 可选
        fileNameExtension: nameExtension,
        photoType: photoAccessHelper.PhotoType.IMAGE,
        // 可选,支持:普通图片、动态图片
        subtype: photoAccessHelper.PhotoSubtype.DEFAULT,
      }];
      let desFileUris: Array<string> = await phAccessHelper.showAssetsCreationDialog(srcFileUris, photoCreationConfigs);
      hilog.info(0x0001, TAG, `showAssetsCreationDialog success, data is:${desFileUris}`);
      if (desFileUris.length == 0) {
        // 用户拒绝保存
        throw (new Error('用户拒绝保存'));
      }
      this.createAssetByIo(uri, desFileUris[0]);
      return Promise.resolve();
    } catch (err) {
      hilog.error(0x0001, TAG, `showAssetsCreationDialog failed, errCode is:${err.code}` + `errMsg is:${err.message}`);
      return Promise.reject(err);
    }
  }

  createAssetByIo(sourceFilePath: string, targetFilePath: string) {
    try {
      hilog.info(0x0001, TAG, `文件路径:${this.context.filesDir}`);
      let srcFile: fileIo.File = fileIo.openSync(sourceFilePath, fileIo.OpenMode.READ_ONLY);
      let targetFile: fileIo.File = fileIo.openSync(targetFilePath, fileIo.OpenMode.READ_WRITE);
      fileIo.copyFileSync(srcFile.fd, targetFile.fd);
      fileIo.closeSync(srcFile);
      fileIo.closeSync(targetFile);
    } catch (error) {
      hilog.error(0x0001, TAG, `showAssetsCreationDialog failed, msg is:${error}`);
    }
  }
}

效果图:

常见FAQ

Q:let desFileUris: Array = await phAccessHelper.showAssetsCreationDialog(srcFileUris, photoCreationConfigs); desFileUris数组中的URI里面返回的是-3006。

A:photoCreationConfigs中的title值,不可以包含:([./:*?"'`<>|{}[]])等这些特殊字符。