文章

图片上传

图片上传

图片上传

图片上传

实现步骤说明

1、媒体读写权限检查和申请\ 2、从手机存储选择图片或拍照\ 3、把图片复制到缓存目录\ 4、接口请求上传图片

所需权限‼️

module.json5:

**ohos.permission.READ_MEDIA**

**�ohos.permission.WRITE_MEDIA**

完整代码

上传工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
import fs from '@ohos.file.fs';
import { checkAppPermission } from './util'
import { HdResponse } from '../request'
import { API_URL, userApi } from '../../apis'
import { authStore } from '../../store';
import { BusinessError } from '@ohos.base';
import camera from '@ohos.multimedia.camera';
import { request } from '@kit.BasicServicesKit';
import camerapicker from '@ohos.multimedia.cameraPicker';
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { Context } from '@ohos.abilityAccessCtrl';
import common from '@ohos.app.ability.common'

//上传回调数据类型
export interface UploadReceiveRes {
  uid: string
  url: string
  name: string
  fileType: string
}

export enum PickerMediaType {
  IMAGE,
  PHOTO
}

class ImageUploadModule {
  private Url_Base: string
  private context = getContext(this) as common.UIAbilityContext; //UIAbilityContext

  constructor(Url_Base: string) {
    this.Url_Base = Url_Base
  }

  //开始上传图片 path:图片路径后缀(图片名称)
  async uploadImage(path: string): Promise<HdResponse<UploadReceiveRes[]>> {
    return new Promise(async (resolve, reject) => {
      const token = authStore.getToken()
      let uri = `internal://cache/${path}` //上传图片全路径
      const files: request.File[] = [
        {
          uri,
          filename: path,
          // 该字段很重要(要和后端保持一致)
          name: "files",
          type: path.split('.')[path.split('.').length-1]
        }
      ]

      let uploadConfig: request.UploadConfig = {
        url: this.Url_Base + userApi.upload,
        header: {
          "Authorization": `Bearer ${token}`
        },
        method: "POST",
        files,
        data: [],
      }

      try {
        let uploadTask: request.UploadTask = await request.uploadFile(getContext(), uploadConfig)
        //上传中回调
        uploadTask.on('progress', (size, total) => {
          console.log('上传进度:', size.toString(), total.toString())
        })

        // 每上传一张图片成功回调
        // 响应体的 header 和 body 都会在这个 header object 里存放
        uploadTask.on('headerReceive', (data: object) => {
          console.info("upOnComplete headerReceive taskState:" + data['body']);
          const result: HdResponse<UploadReceiveRes[]> = JSON.parse(data['body'])
          resolve(result)
        })

        //所有上传完成回调
        uploadTask.on('complete', (taskStates: request.TaskState[]) => {
          console.info("upOnComplete complete taskState:" + JSON.stringify(taskStates));
        })
        //上传失败回调
        uploadTask.on('fail', (taskStates: request.TaskState[]) => {
          console.info("upOnComplete fail taskState:" + JSON.stringify(taskStates));
          reject()
        })
      } catch (e) {
        console.log(JSON.stringify(e), 'e')
      }
    })
  }

  // 上传图片 拍照/相册上传
  async imageUpload(type: PickerMediaType): Promise<HdResponse<UploadReceiveRes[]>> {
    return new Promise(async (resolve, reject) => {
      //检查是否有读写外部媒体权限
      let res: boolean = await checkAppPermission(this.context)
      //无权限返回
      if (!res) {
        return
      }
      // 从相册选择
      if (type === PickerMediaType.IMAGE) {
        const photoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
        photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE; // 过滤选择媒体文件类型为IMAGE
        photoSelectOptions.maxSelectNumber = 1; // 选择媒体文件的最大数目
        const photoViewPicker = new photoAccessHelper.PhotoViewPicker();
        const photoSelectResult = await photoViewPicker.select(photoSelectOptions)
        if (photoSelectResult.photoUris.length) {
          //复制图片到缓存目录(缓存目录才有读写权限)
          let filePath = await copyFileToCache(photoSelectResult.photoUris[0], this.context)
          if (filePath) {
            this.uploadImage(filePath)
              .then((result) => {
                resolve(result)
              })
              .catch(() => reject())
          }
        }
      }
      // 拍照
      if (type === PickerMediaType.PHOTO) {
        try {
          let pickerProfile: camerapicker.PickerProfile = {
            cameraPosition: camera.CameraPosition.CAMERA_POSITION_BACK
          };
          let pickerResult: camerapicker.PickerResult =
            await camerapicker.pick(this.context, [camerapicker.PickerMediaType.PHOTO], pickerProfile);
          if (pickerResult?.resultUri) {
            //复制图片到缓存目录(缓存目录才有读写权限)
            let filePath = await copyFileToCache(pickerResult.resultUri, this.context)
            if (filePath) {
              //上传头像并设置
              this.uploadImage(filePath)
                .then((result) => resolve(result))
                .catch(() => reject())
            }
          }
        } catch (error) {
          let err = error as BusinessError;
          console.error(`the pick call failed. error code: ${err.code}`);
        }
      }
    })
  }
}

/**
 * 复制文件到缓存目录下
 * @param path :文件路径
 * @param context :Context
 * @returns Promise<string> 移动后文件路径
 */
async function copyFileToCache(path: string, context: Context): Promise<string> {
  try {
    let file = fs.openSync(path, fs.OpenMode.READ_ONLY) //只读
    if (file) {
      let fileDir: string = `${context.cacheDir}` //临时文件目录
      //时间戳生成随机文件名
      let newPath: string = `${new Date().getTime()}_${path.split("/")[path.split("/").length-1]}`
      let targetPath: string = `${fileDir}/${newPath}`
      fs.copyFileSync(file.fd, targetPath)
      return newPath
    } else {
      return ''
    }
  } catch (e) {
    return Promise.resolve('')
  }
}

// 实例化图片上传实例,然后导出
export const UploadModule = new ImageUploadModule(API_URL)

其他工具函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
import { common } from '@kit.AbilityKit';
import bundleManager from '@ohos.bundle.bundleManager';
import abilityAccessCtrl, { Permissions } from '@ohos.abilityAccessCtrl';

interface rejectObj {
  code: number
  message: string
}

//检查权限
export async function checkAppPermission(context: common.UIAbilityContext): Promise<boolean> {
  try {
    const READ_MEDIA_PERMISSION: Permissions = 'ohos.permission.READ_MEDIA' //媒体读取权限
    const WRITE_MEDIA_PERMISSION: Permissions = 'ohos.permission.WRITE_MEDIA' //媒体写入权限
    let permissionList: Permissions[] = []; //需要申请选项列表
    let readPermission = await checkPermissions(READ_MEDIA_PERMISSION) //检查是否有媒体读取权限
    !readPermission && permissionList.push(READ_MEDIA_PERMISSION)
    let writePermission = await checkPermissions(WRITE_MEDIA_PERMISSION) //检查是否有媒体写入权限
    !writePermission && permissionList.push(READ_MEDIA_PERMISSION)

    console.log('5555', permissionList.length.toString())
    if (permissionList.length) {
      //申请权限
      let res: boolean = await applyPermission(context, permissionList)
      if (!res) { //用户未同意授权
        AlertDialog.show({
          title: "提示",
          message: "无权限读写用户外部存储中的媒体文件信息,请前往系统设置开启",
          alignment: DialogAlignment.Center,
          secondaryButton: {
            value: '关闭',
            action: () => {
            }
          }
        })
      }
      return res
    }
    return true

  } catch (e) {
    return Promise.reject(e)
  }
}

/**
 * 申请权限
 * @params context:AblitiyContext
 * @params permissions:权限名称数组
 * @returns Promise<boolean>:是否授权成功
 */
async function applyPermission(context: common.UIAbilityContext,
  permissions: Array<Permissions>): Promise<boolean> {
  let atManager = abilityAccessCtrl.createAtManager();
  return new Promise((resolve: (res: boolean) => void, reject: (e: rejectObj) => void) => {
    atManager.requestPermissionsFromUser(context, permissions).then((data) => {
      let grantStatus: Array<number> = data.authResults;
      resolve(grantStatus.every(item => item === 0))
    }).catch((err: rejectObj) => {
      reject(err)
    })

  })
}

//检查用户权限
//@params permissions:权限名称数组
async function checkPermissions(permissions: Permissions): Promise<boolean> {
  try {
    let grantStatus: abilityAccessCtrl.GrantStatus = await checkAccessToken(permissions);
    return grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED
  } catch (e) {
    return Promise.reject(e)
  }
}

//校验应用是否授予权限
//@params permissions:权限名称数组
//@return permissionabilityAccessCtrl:权限名称
async function checkAccessToken(permission: Permissions): Promise<abilityAccessCtrl.GrantStatus> {
  let atManager = abilityAccessCtrl.createAtManager();
  let grantStatus: abilityAccessCtrl.GrantStatus = 0;

  // 获取应用程序的accessTokenID
  let tokenId: number = 0;
  try {
    let bundleInfo: bundleManager.BundleInfo =
      await bundleManager.getBundleInfoForSelf(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION);
    let appInfo: bundleManager.ApplicationInfo = bundleInfo.appInfo;
    tokenId = appInfo.accessTokenId;
  } catch (err) {
    console.error(`getBundleInfoForSelf failed, code is ${err.code}, message is ${err.message}`);
  }

  // 校验应用是否被授予权限
  try {
    grantStatus = await atManager.checkAccessToken(tokenId, permission);
  } catch (err) {
    console.error(`checkAccessToken failed, code is ${err.code}, message is ${err.message}`);
  }

  return grantStatus;
}

注意

一开始我的模拟器一直报 13400001 错误(文件操作权限) 解决:Wipe User Data(清楚用户缓存)或者 删除模拟器重新安装

1732607278206-15a593a5-90e4-467a-97a5-eda76274c9fd.png

本文由作者按照 CC BY 4.0 进行授权