这篇文章主要介绍如何在鸿蒙应用开发中实现选择本地图库照片并上传至服务器的功能。将通过三个主要步骤:选择图片、拷贝到缓存目录、上传到服务器,来解释l整个过程,在关键步骤中加入必要的注意事项和坑点。
模拟器不能正确显示但截图后第一个位置有图片
第一步:用户选择图片
在鸿蒙系统中,使用PhotoViewPicker
对象来实现图片选择功能。代码示例中定义了一个pickerAvatar
函数,负责引导用户选择一张图片,并返回该图片的内存地址(URI)。
代码解析:
tsconst options = new picker.PhotoSelectOptionsoptions.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE // 指定只能选择图片类型options.maxSelectNumber = 1 // 用户只能选择一张图片
-
注意:设置
MIMEType
确保用户只能选择图片,防止选择其他文件类型导致程序错误。
tsconst pickerView = new picker.PhotoViewPicker()let urls = await pickerView.select(options)if (urls.photoUris.length <= 0) { return}let imgUrl = urls.photoUris[0]return imgUrlAlertDialog.show({ message: imgUrl })
-
注意:务必检查
urls.photoUris
的长度,确保用户真的选择了图片。若没有选择图片,则函数提前退出。
第二步:拷贝图片到缓存目录
用户选择的图片通常位于只读存储中,需要将其拷贝到应用的缓存目录,以便后续处理。
代码解析:
tsconst file = fs.openSync(photoImgPath, fs.OpenMode.READ_ONLY) // 打开文件为只读模式let fileFD = file.fdlet destPath = getContext().cacheDir // 获取缓存目录路径let fileName = Date.now().toString() + '.jpg' // 生成新的文件名let fullPath = destPath + '/' + fileNameAlertDialog.show({ message: '完整路径' + fullPath })fs.copyFileSync(fileFD, fullPath) // 拷贝文件return [`internal://cache/${fileName}`, fileName]
-
注意:生成的文件名要确保唯一,避免覆盖已有文件。使用时间戳可以部分解决这个问题,但在高并发场景下仍可能有冲突。
第三步:上传图片到服务器
得到缓存目录中的图片文件后,通过网络请求将其上传至服务器。在上传过程中,我们可以通过日志或者UI来监听上传进度(日志示例)。
代码解析:
tslet user = AppStorage.get('user') as ILoginUsersModellet token = user?.tokenlet uploader = await request.uploadFile(getContext(), { method: 'POST', url: 'https://teach.itheima.net/hm/userInfo/avatar', header: { 'Content-Type': 'multipart/form-data', 'Authorization': `Bearer ${token}` }, files: [{ filename: arr[1], type: 'jpg', name: 'file', uri: arr[0] }], data: [] })uploader.on('progress', (uploadedSize, totalSize) => { Logger.info('上传大小' + uploadedSize, '总大小:' + totalSize) if (uploadedSize === totalSize) { AlertDialog.show({ message: '上传已完成' }) }})
-
注意:在上传文件时,监听上传进度可以用于调试,还可以增强用户交互体验。通过
progress
事件,我们能够实时更新用户界面,展示上传进度。
总结
上面案例中在鸿蒙系统开发中实现从选择图片到上传图片,并增加了进度监控功能。在实现时应注意文件类型的正确性、文件命名的唯一性、网络请求的安全性。
完整封装函数代码:
//1.用户选择图片后返回一个内存地址async pickerAvatar() { //1.引导用户选择一张系统相册照片 //1.1绑定用户只能从系统相册选择一张图片 const options = new picker.PhotoSelectOptions options.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE //只能选择相片资源 options.maxSelectNumber = 1 //最大选择图片数目 //1.2 利用PhotoViewPicker对象实例中的select自动获取到用户选择的那张图片的地址 const pickerView = new picker.PhotoViewPicker() let urls = await pickerView.select(options) if (urls.photoUris.length <= 0) { return } let imgUrl = urls.photoUris[0] return imgUrl AlertDialog.show({ message: imgUrl })}//2.利用已获得的内存地址将系统相册的图片拷贝到缓存目录async copyImgToCache(photoImgPath: string) { // // 2.1 根据源地址获取文件的磁盘目录,通过openSync()来获得的内存地址 const file = fs.openSync(photoImgPath, fs.OpenMode.READ_ONLY) let fileFD = file.fd //2.2利用fs拷贝到目标地址 //获取目标地址文件夹目录 let destPath = getContext().cacheDir //随机生成一个文件名 let fileName = Date.now().toString() //固定一个文件扩展名 const ext = 'jpg' //组合成一个完整的目标文件路径 let fullPath = destPath + '/' + fileName + '.' + ext AlertDialog.show({ message: '完整路径' + fullPath }) fs.copyFileSync(fileFD, fullPath) //internal://cache代表fullPath 的文件路径 return [`internal://cache/${fileName + '.' + ext}`, fileName + '.' + ext]}//3.将获取到的图片上传到服务器async uploadAvatar(arr: string[]) { let user = AppStorage.get('user') as ILoginUsersModel let token = user?.token let uploader = await request.uploadFile(getContext(), { method: 'POST', url: 'https://teach.itheima.net/hm/userInfo/avatar', header: { 'Content-Type': 'multipart/form-data', 'Authorization': `Bearer ${token}` }, files: [{ filename: arr[1], type: 'jpg', name: 'file', uri: arr[0] }], data: [] }) uploader.on('progress', (uploadedSize, totleSize) => { Logger.info('上传大小' + uploadedSize, '总大小:' + totleSize) if (uploadedSize === totleSize) { AlertDialog.show({ message: '上传已完成' }) } })}