[Hongmeng Application ArkTS Development Series] - Select pictures, files and camera functions to implement

##Preface

When using apps, we often send multimedia files such as pictures or files when chatting in some social software. So how do we develop such a function in Hongmeng native apps? This article will explain this function point to you. We use an implementation method of pulling up the system components to select pictures and files, and pulling up the system camera to take pictures.

Create a multimedia demo project

We use the Empty template to create a Demo project.

Create MediaBean entity class

Create the bean folder under src->main->ets and create the MediaBean.ts file under the folder a>

/**
 * 多媒体数据类
 */
export class MediaBean {
  /**
   * 文件名称
   */
  public fileName: string;
  /**
   * 文件大小
   */
  public fileSize: number;
  /**
   * 文件类型
   */
  public fileType: string;
  /**
   * 本地存储地址
   */
  public localUrl: string;
}

Create MediaHelper tool class

Create a helper folder under src->main->ets, and create a MediaHelper.ts file under the folder.

The complete code of MediaHelper.ts is as follows:

import common from '@ohos.app.ability.common';
import picker from '@ohos.file.picker';
import mediaLibrary from '@ohos.multimedia.mediaLibrary';
import wantConstant from '@ohos.ability.wantConstant';
import { MediaBean } from '../bean/MediaBean';
import { StringUtils } from '../utils/StringUtils';
import { Log } from '../utils/Log';

/**
 * 多媒体辅助类
 */
export class MediaHelper {
  private readonly TAG: string = 'MediaHelper';

  private mContext: common.Context;

  constructor(context: common.Context) {
    this.mContext = context;
  }

  /**
   * 选择图片
   */
  public selectPicture(): Promise<MediaBean> {

    try {
      let photoSelectOptions = new picker.PhotoSelectOptions();
      photoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE;
      photoSelectOptions.maxSelectNumber = 1;
      let photoPicker = new picker.PhotoViewPicker();
      return photoPicker.select(photoSelectOptions)
        .then((photoSelectResult) => {
          Log.info(this.TAG, 'PhotoViewPicker.select successfully, PhotoSelectResult uri: ' + JSON.stringify(photoSelectResult));

          if (photoSelectResult && photoSelectResult.photoUris && photoSelectResult.photoUris.length > 0) {
            let filePath = photoSelectResult.photoUris[0];
            Log.info(this.TAG, 'PhotoViewPicker.select successfully, PhotoSelectResult uri: ' + filePath);
            return filePath;
          }

        }).catch((err) => {
          Log.error(this.TAG, 'PhotoViewPicker.select failed with err: ' + err);
          return err;
        }).then(async (filePath) => {
          const mediaBean = await this.buildMediaBean(filePath);
          return mediaBean;
        });
    } catch (err) {
      Log.error(this.TAG, 'PhotoViewPicker failed with err: ' + err);
      return Promise.reject(err);
    }
  }

  /**
   * 选择文件
   */
  public selectFile(): Promise<MediaBean> {
    try {
      let documentSelectOptions = new picker.DocumentSelectOptions();
      let documentPicker = new picker.DocumentViewPicker();
      return documentPicker.select(documentSelectOptions)
        .then((documentSelectResult) => {
          Log.info(this.TAG, 'DocumentViewPicker.select successfully, DocumentSelectResult uri: ' + JSON.stringify(documentSelectResult));

          if (documentSelectResult && documentSelectResult.length > 0) {
            let filePath = documentSelectResult[0];
            Log.info(this.TAG, 'DocumentViewPicker.select successfully, DocumentSelectResult uri: ' + filePath);
            return filePath;
          }

        }).catch((err) => {
          Log.error(this.TAG, 'PhotoViewPicker.select failed with err: ' + err);
          return err;
        }).then(async (filePath) => {

          const mediaBean = await this.buildMediaBean(filePath);
          return mediaBean;

        });
    } catch (err) {
      Log.error(this.TAG, 'PhotoViewPicker failed with err: ' + err);
      return Promise.reject(err);
    }
  }

  /**
   * 拍照
   */
  public async takePhoto(context: common.UIAbilityContext): Promise<MediaBean> {

    let want = {
      'uri': '',
      'action': wantConstant.Action.ACTION_IMAGE_CAPTURE,
      'parameters': {},
    };
    return context.startAbilityForResult(want)
      .then((result) => {
        Log.info(this.TAG, `startAbility call back , ${JSON.stringify(result)}`);
        if (result.resultCode === 0 && result.want && StringUtils.isNotNullOrEmpty(result.want.uri)) {
          //拍照成功
          Log.info(this.TAG, 'takePhoto successfully, takePhotoResult uri: ' + result.want.uri);
          return result.want.uri;
        }
      }).catch((error) => {
        Log.info(this.TAG, `startAbility error , ${JSON.stringify(error)}`);
        return error;
      }).then(async (uri: string) => {
        const mediaBean = await this.buildMediaBean(uri);
        return mediaBean;
      });
  }

  /**
   * 封装多媒体实体类
   *
   * @param uri 文件路径
   */
  private async buildMediaBean(uri: string): Promise<MediaBean> {

    if (StringUtils.isNullOrEmpty(uri)) {
      return null;
    }

    const mediaBean: MediaBean = new MediaBean();
    mediaBean.localUrl = uri;
    await this.appendFileInfoToMediaBean(mediaBean, uri);
    return mediaBean;
  }

  /**
   * 通过Uri查找所选文件信息,插入到MediaBean中
   * @param mediaBean
   * @param uri
   */
  private async appendFileInfoToMediaBean(mediaBean: MediaBean, uri: string) {

    if (StringUtils.isNullOrEmpty(uri)) {
      return;
    }
    let fileList: Array<mediaLibrary.FileAsset> = [];

    const parts: string[] = uri.split('/');
    const id: string = parts.length > 0 ? parts[parts.length - 1] : '-1';

    try {

      let media = mediaLibrary.getMediaLibrary(this.mContext);
      let mediaFetchOptions: mediaLibrary.MediaFetchOptions = {
        selections: mediaLibrary.FileKey.ID + '= ?',
        selectionArgs: [id],
        uri: uri
      };

      let fetchFileResult = await media.getFileAssets(mediaFetchOptions);
      Log.info(this.TAG, `fileList getFileAssetsFromType fetchFileResult.count = ${fetchFileResult.getCount()}`);
      fileList = await fetchFileResult.getAllObject();
      fetchFileResult.close();
      await media.release();

    } catch (e) {
      Log.error(this.TAG, "query: file data  exception ");
    }

    if (fileList && fileList.length > 0) {

      let fileInfoObj = fileList[0];
      Log.info(this.TAG, `file id = ${JSON.stringify(fileInfoObj.id)} , uri = ${JSON.stringify(fileInfoObj.uri)}`);
      Log.info(this.TAG, `file fileList displayName = ${fileInfoObj.displayName} ,size = ${fileInfoObj.size} ,mimeType = ${fileInfoObj.mimeType}`);

      mediaBean.fileName = fileInfoObj.displayName;
      mediaBean.fileSize = fileInfoObj.size;
      mediaBean.fileType = fileInfoObj.mimeType;

    }
  }
}

The MediaHelper class defines 5 methods,

  • selectPicture provides the function of selecting pictures

     我们通过系统组件 picker.PhotoViewPicker 来进行图片选择,通过配置PhotoSelectOptions,指定选择的MIMEType类型(这里PhotoViewMIMETypes.IMAGE_TYPE 图片类型) 、选择的图片最大数量 maxSelectNumber ,这里我们实现单选功能,数值设置为1即可。使用photoPicker.select 拉起系统组件进行选择,然后在回调中获取图片的uri。
    
  • selectFile provides the function of selecting files

    For the function of selecting files, we use the system component picker.DocumentViewPicker to select files. The code is basically the same as that of image selection. The difference is DocumentSelectOptions. Currently, api9 does not provide configuration items. Please pay attention to the subsequent api version.

  • takePhoto provides photo taking function

    For the function of taking pictures, we also pull up the camera to take pictures. We use the startAbilityForResult method + configure the pull-up action (wantConstant.Action.ACTION_IMAGE_CAPTURE) to pull up the system camera. After taking the picture, we receive the returned data in then. We Use the return code result.resultCode to determine whether a photo was taken. If the status value ===0, it means a photo was taken. We then use result.want.uri to obtain the uri of the photo after taking the photo.

  • buildMediaBean internal method, providing MediaBean object encapsulation

    The main function of this method is to encapsulate a multimedia entity class and trigger appendFileInfoToMediaBean to obtain some file information of the file corresponding to the Uri. The code is very simple and I believe everyone can understand it at a glance.

  • The appendFileInfoToMediaBean internal method provides the function of appending the file information of the selected file.

    The main function of this method is to query the detailed information of the file through the uri, including file name, file size, and file type. Get the file ID via Uri. Use mediaLibrary.getMediaLibrary to get the media object. Configure MediaFetchOptions, mainly ID, to find file objects through file IDs. Use media.getFileAssets to query the file object results. This can be a batch operation to get a FetchFileResult object. Traverse the FileAsset array to get file information.

    Here are some fields of FileAsset:

After selecting pictures, files or taking photos through system components, the system simply returns the Uri of a file. If we need to display the file name, file size, and file type, we need to obtain it separately through the appendFileInfoToMediaBean method.

API tag deprecation issue

The above code can be used normally in api9. However, some APIs are marked as expired. Some of them are stated in the official documents to be discontinued soon, but I have not found any APIs that can be replaced. If any readers know about it, please let me know. Let me know in the comment section, thank you.

  1. ohos.app.ability.wantConstant

    The official prompt asks us to switch to the ohos.app.ability.wantConstant class, but we use wantConstant.Action. This Action is not defined in ohos.app.ability.wantConstant, and I did not find where the Action is in the SDK. defined in class;

  2. mediaLibrary.getMediaLibrary.getFileAssets

    We need to use getMediaLibrary to obtain multimedia objects and call getFileAssets to query the multimedia information of the file. The official prompts us to use ohos.file.picker. The strange thing is that there is no getFileAssets related method in picker. I don’t know what the official considerations are based on. Maybe API 10 will increase it in the future. Please support the corresponding method. Then we can only get the Uri of a file through picker. We cannot get the regular file-related data such as file name and file size, and the function cannot be developed. This is also a question I had before.

Dynamically apply for multimedia access permissions

To read the multimedia information of the file, we need to apply for a multimedia read permission ohos.permission.READ_MEDIA. This permission needs to be in

Add the configuration requestPermissions in module.json5, and configure the READ_MEDIA permission under this node, as shown below:

Since this READ_MEDIA permission requires dynamic permission application, we also need to develop the dynamic permission application code logic. Due to space reasons, I will not go into details here. If there is anything unclear about this dynamic permission application in the future, I will try again. Write an article to introduce how to implement the functions of dynamically applying for permissions and jumping to the system permission setting page to configure permissions.

For this demo, we directly install it, find the application in the system settings, and enable the corresponding permissions (bypassing dynamic application for permissions).

Implement the function of selecting picture display

Next we write the UI page, use our MediaHelper tool class above to select pictures, take pictures, and display the pictures.

We put three buttons in the Index.ets file, as well as controls to display the file name, size, file type and file path, and display pictures.
The complete code is as follows:

import common from '@ohos.app.ability.common';
import { MediaBean } from '../bean/MediaBean';
import { MediaHelper } from '../helper/MediaHelper';

@Entry
@Component
struct Index {
  @State mediaBean: MediaBean = new MediaBean();
  private mediaHelper: MediaHelper = new MediaHelper(getContext());

  build() {
    Row() {
      Column() {
        Text('选择图片')
          .textAlign(TextAlign.Center)
          .width(200)
          .fontSize(16)
          .padding(10)
          .margin(20)
          .border({ width: 0.5, color: '#ff38f84b', radius: 15 })
          .onClick(() => {
            this.handleClick(MediaOption.Picture)
          })

        Text('选择文件')
          .textAlign(TextAlign.Center)
          .width(200)
          .fontSize(16)
          .padding(10)
          .margin(20)
          .border({ width: 0.5, color: '#ff38f84b', radius: 15 })
          .onClick(() => {
            this.handleClick(MediaOption.File)
          })

        Text('拍照')
          .textAlign(TextAlign.Center)
          .width(200)
          .fontSize(16)
          .padding(10)
          .margin(20)
          .border({ width: 0.5, color: '#ff38f84b', radius: 15 })
          .onClick(() => {
            this.handleClick(MediaOption.TakePhoto)
          })

        Divider()
          .width('100%')
          .height(0.5)
          .color('#ff99f6a2')
          .margin({ top: 20 })
          .padding({ left: 20, right: 20 })

        Text(`文件名称: ${this.mediaBean.fileName ? this.mediaBean.fileName : ''}`)
          .textAlign(TextAlign.Center)
          .width('100%')
          .fontSize(16)
          .margin(10)

        Text(`文件大小: ${this.mediaBean.fileSize ? this.mediaBean.fileSize : ''}`)
          .textAlign(TextAlign.Center)
          .width('100%')
          .fontSize(16)
          .margin(10)

        Text(`文件类型: ${this.mediaBean.fileType ? this.mediaBean.fileType : ''}`)
          .textAlign(TextAlign.Center)
          .width('100%')
          .fontSize(16)
          .margin(10)

        Text(`文件Uri: ${this.mediaBean.localUrl ? this.mediaBean.localUrl : ''}`)
          .textAlign(TextAlign.Center)
          .width('100%')
          .fontSize(16)
          .margin(10)

        Image(this.mediaBean.localUrl)
          .width(300)
          .height(300)
          .backgroundColor(Color.Grey)

      }
      .width('100%')
      .height('100%')
    }
    .height('100%')
  }

  async handleClick(option: MediaOption) {
    let mediaBean: MediaBean;
    switch (option) {
      case MediaOption.Picture:
        mediaBean = await this.mediaHelper.selectPicture();
        break;
      case MediaOption.File:
        mediaBean = await this.mediaHelper.selectFile();
        break;
      case MediaOption.TakePhoto:
        mediaBean = await this.mediaHelper.takePhoto(getContext() as common.UIAbilityContext);
        break;
      default:
        break;
    }

    if (mediaBean) {

      this.mediaBean = mediaBean;

    }

  }
}

enum MediaOption {
  Picture = 0,
  File = 1,
  TakePhoto = 2
}

Package testing

To package and install it on a real machine, we need to configure signature information for the project. We click File -> Project Structure ->Project, select the Signing Configs panel, check Support HarmonyOS and Automatically generate signature to automatically generate the debugging signature. After the generation is completed, run and install it on the mobile phone.

Note: Since we have not implemented dynamic application for multimedia reading permissions, you need to find the application in the mobile phone system settings - applications and enable multimedia permissions. This permission is disabled by default. After enabling it, you can open the application again. The specific effect of the operation is as shown in the picture at the beginning of the article.

"Harmony OS Development Learning Manual"

Must-see for getting started:https://qr21.cn/FV7h05

  1. Application Development Guide (ArkTS)
  2. Introduction to Application Development (Java)

HarmonyOS concept:https://qr21.cn/FV7h05

  1. system definition
  2. Technology Architecture
  3. Technical characteristics
  4. system security

How to get started quickly:https://qr21.cn/FV7h05

  1. basic concept
  2. Building your first ArkTS application
  3. Build your first JS application
  4. ……

Development basics:https://qr21.cn/FV7h05

  1. Application basics
  2. Configuration file
  3. Application data management
  4. Application security management
  5. App privacy protection
  6. Third-party application call control mechanism
  7. Resource classification and access
  8. Learn the ArkTS language
  9. ……

Developed based on ArkTS:https://qr21.cn/FV7h05

  1. Ability development
  2. UI opening
  3. Public events and notifications
  4. window management
  5. media
  6. Safety
  7. Networks and Links
  8. phone service
  9. Data management
  10. Background Task Management
  11. Device management
  12. Equipment usage information statistics
  13. DFX
  14. international development
  15. Folding screen series
  16. ……

Guess you like

Origin blog.csdn.net/maniuT/article/details/134859552