HarmonyOS开发 - 上传图片和拍照组件封装
·
大多数app项目基本上都设计到拍照和图片上传,尤其是用户编辑个人头像或者发布动态,
下面,我以最简单的方式封装了一个ImgUploadDialog.ets。
效果如下:

好的,那我们来分析一下,拍照是不是需要权限呢?选取本地文件(图片)是不是也要权限呢?
所以我查阅了官方api,总结我们需要使用
@ohos.abilityAccessCtrl:权限管理能力,包括鉴权、授权等
@ohos.bundle.bundleManager:获取BundleInfo,然后拿到appInfo,就可以拿accessTokenId
@ohos.file.fs:文件控制
分析好了之后就开干,先新建个工具类,utils.ets:
import fs from '@ohos.file.fs';
import bundleManager from '@ohos.bundle.bundleManager';
import abilityAccessCtrl, { Context, Permissions } from '@ohos.abilityAccessCtrl';
import common from '@ohos.app.ability.common'
/**
* 复制文件到缓存目录下
* @param path :文件路径
* @param context :Context
* @returns Promise<string> 移动后文件路径
*/
export async function copyFileToCache(path: string,context:Context): Promise<string> {
try {
let file = fs.openSync(path, fs.OpenMode.READ_WRITE)
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('')
}
}
//校验应用是否授予权限
//@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;
}
//检查用户权限
//@params permissions:权限名称数组
export 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)
}
}
interface rejectObj {
code: number
message: string
}
/**
* 申请权限
* @params context:AblitiyContext
* @params permissions:权限名称数组
* @returns Promise<boolean>:是否授权成功
*/
export 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)
})
})
}
@Concurrent
export function printArgs(type: number): Promise<string> {
return new Promise((rs,rj) =>{
console.log('任务内容')
if (type % 2 === 0) {
rj('FAIL FULL')
}
setTimeout(() =>{
rs('SUCCESS FULL')
}, 2000)
})
}
方法都写好了,在去新建个组件:ImgUploadDialog.ets
import picker from '@ohos.file.picker';
import { checkPermissions, applyPermission, copyFileToCache } from '../utils/index'
import { request } from '@kit.BasicServicesKit';
import { Permissions } from '@ohos.abilityAccessCtrl';
import camera from '@ohos.multimedia.camera';
import camerapicker from '@ohos.multimedia.cameraPicker';
import { BusinessError } from '@ohos.base';
import { common } from '@kit.AbilityKit';
//上传回调数据类型
interface ReceiveRes {
body: string
headers: object
}
@Extend(Text)
function custText() {
.width('100%')
.height('48')
.fontColor('#39364D')
.textAlign(TextAlign.Center)
}
@CustomDialog
export default struct ImageUploadDialog {
dialogController: CustomDialogController
@Prop uploadURL:string='';//上传接口地址
private context = getContext(this) as common.UIAbilityContext; //UIAbilityContext
private success: (res: ReceiveRes) => void = () => {} //上传成功回调
private fail: (res: request.TaskState[]) => void = () => {} //上传失败回调
private complete: (res: request.TaskState[]) => void = () => {} //上传完成回调
//检查权限
async checkAppPermission(): Promise<boolean> {
try {
const READ_MEDIA_PERMISSION: Permissions = 'ohos.permission.READ_MEDIA' //媒体读取权限
const WRITE_MEDIA_PERMISSION: Permissions = 'ohos.permission.WRITE_MEDIA' //媒体写入权限
const CAMERA_PERMISSION: Permissions = 'ohos.permission.CAMERA' //媒体写入权限
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)
let cameraPermission = await checkPermissions(CAMERA_PERMISSION)//检查是否有媒体写入权限
!cameraPermission && permissionList.push(CAMERA_PERMISSION)
if (permissionList.length) {
//申请权限
let res: boolean = await applyPermission(this.context, permissionList)
if (!res) {//用户未同意授权
AlertDialog.show({
title: "提示",
message: "无权限读写用户外部存储中的媒体文件信息,请前往系统设置开启",
alignment: DialogAlignment.Center,
secondaryButton: {
value: '关闭',
action: () => {
}
}
})
}
return res
}
return true
}
catch (e) {
return Promise.reject(e)
}
}
//开始上传图片 path:图片路径后缀(图片名称)
async uploadImage(path: string) {
console.log(path, 'path')
let uri=`internal://cache/${path}` //上传图片全路径
let uploadConfig: request.UploadConfig = {
url:this.uploadURL,
header:{},
method: "POST",
files: [{ filename: path, name: "file", uri, type: path.split('.')[path.split('.').length-1] }],
data: [],
};
try {
let uploadTask:request.UploadTask=await request.uploadFile(this.context, uploadConfig)
//上传中回调
uploadTask.on('progress', (size,total) => {
console.log(size.toString(),total.toString(),'上传进度')
})
//每上传一张图片成功回调
uploadTask.on('headerReceive', (data: object) => {
let res = data as ReceiveRes
this.success && this.success(res)
})
//所有上传完成回调
uploadTask.on('complete', (taskStates: request.TaskState[]) => {
console.info("upOnComplete complete taskState:" + JSON.stringify(taskStates));
this.complete && this.complete(taskStates)
})
//上传失败回调
uploadTask.on('fail', (taskStates: request.TaskState[]) => {
console.info("upOnComplete fail taskState:" + JSON.stringify(taskStates));
this.fail&&this.fail(taskStates)
})
}catch (e){
console.log( JSON.stringify(e),'e')
}
}
build() {
Column() {
//拍照
Text('拍照').custText().onClick(async()=>{
//检查是否有读写外部媒体权限
let res: boolean = await this.checkAppPermission()
//无权限返回
console.log(JSON.stringify(res), '拍照是否有权限')
if (!res) {
return
}
try {
let pickerProfile: camerapicker.PickerProfile = {
cameraPosition: camera.CameraPosition.CAMERA_POSITION_BACK
};
let pickerResult: camerapicker.PickerResult = await camerapicker.pick(this.context,
[camerapicker.PickerMediaType.PHOTO, camerapicker.PickerMediaType.PHOTO], pickerProfile);
if(pickerResult?.resultUri){
//关闭弹窗
this.dialogController.close()
//复制图片到缓存目录(缓存目录才有读写权限)
let filePath = await copyFileToCache(pickerResult.resultUri, this.context)
if (filePath) {
//上传头像并设置
this.uploadImage(filePath)
}
}
} catch (error) {
let err = error as BusinessError;
console.error(`the pick call failed. error code: ${err.code}`);
}
})
Divider().color('#F7F9FA').width('100%').strokeWidth(1)
//从手机相册选择
Text('从手机相册选择').custText().onClick(async () => {
//检查是否有读写外部媒体权限
let res: boolean = await this.checkAppPermission()
//无权限返回
if (!res) return
//关闭弹窗
this.dialogController.close()
//从相册选择
let PhotoSelectOptions = new picker.PhotoSelectOptions();
PhotoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE;
PhotoSelectOptions.maxSelectNumber = 1;
let photoPicker = new picker.PhotoViewPicker();
photoPicker.select(PhotoSelectOptions).then(async (PhotoSelectResult) => {
if (PhotoSelectResult.photoUris.length) {
//复制图片到缓存目录(缓存目录才有读写权限)
let filePath = await copyFileToCache(PhotoSelectResult.photoUris[0],this.context)
if (filePath) {
this.uploadImage(filePath)
}
}
})
})
Button('取消', { type: ButtonType.Capsule })
.backgroundColor('#F7F7F7')
.fontSize('16fp')
.fontColor('#333333')
.width('100%')
.margin({ top: '30' })
.onClick(() => {
this.dialogController.close()
})
}.width('100%').padding({ left: '16', top: '11', right: '16', bottom: '16' })
.backgroundColor(Color.White)
.borderRadius({
topLeft: '24',
topRight: '24'
})
}
}
在你需要的地方去使用:
先引入:
import ImgUploadDialog from '../../components/ImgUploadDialog'
在给它初始化一下:
@State dialogController: CustomDialogController | null = null //选择上传类型弹窗控制器
this.dialogController = new CustomDialogController({
builder: ImgUploadDialog({
uploadURL: 'https://xxx/', //上传地址
success: e => { //上传成功回调,e上传成功接口返回数据
let res = JSON.parse(e.body) as object //接口上传成功返回数据
console.log(JSON.stringify(res), '上传成功')
//根据实际接口返回字段获取图片url
//url=res['data']
},
fail: e => { //上传失败回调
console.log(JSON.stringify(e))
promptAction.showToast({ message: '上传失败' })
},
complete: e => { //上传完成回调
console.log(JSON.stringify(e), 'complete')
}
}),
alignment: DialogAlignment.Bottom, //弹窗居于底部
customStyle: true, //自定义样式
})
最后在你想要的方法中掉用:
this.dialogController?.open()更多推荐



所有评论(0)