HarmonyOS上传文件以及权限授权

搞了一天,差不多搞完了,记录下,目前官方还有一些问题 会在下一个next版本修复(使用api9 版本 新版本自行参考修改)

0、实现效果

鸿蒙实现图片上传

1、准备页面

build() {
    Row() {
      Column() {
        Button('选择图片28')
          .onClick(this.onChooseImage)
        // 显示选中的图片(本地地址 非http)
        ForEach(this.showChooseImage, (item, idnex) => {
          Image(item).width(80).height(80).margin({bottom: 20})
        })
        // 为了方便展示选中的图片信息
        ForEach(this.showListData, (item, idnex) => {
          Text(item.path)
        })
      }
      .width('100%')
    }
    .height('100%')
  }

2、授权逻辑

点击“选中图片28”之后,需要先判断是否有读写图片的权限。如果有走后面的流程,如果没有,需要系统弹窗让用户去授权。如果用户授权了,走后面的逻辑;如果用户不授权需要提示用户去系统里面的应用列表去授权(也可以不做这个操作)。

官方文档:文档中心

3、判断是否授权媒体

import abilityAccessCtrl, { Permissions } from '@ohos.abilityAccessCtrl';
let atManager = abilityAccessCtrl.createAtManager();
import bundleManager from '@ohos.bundle.bundleManager';
// let bundleFlags = bundleManager.BundleFlag.GET_BUNDLE_INFO_DEFAULT;
let bundleFlags = bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION;
// let tokenID = bundleFlags;
let tokenID
import common from '@ohos.app.ability.common';
import picker from '@ohos.file.picker';
import request from '@ohos.request';
let context = getContext(this) as common.UIAbilityContext;
/**
 * 对应用权限进行校验封装 我这边默认只能一个一个授权,多个授权自己封装
 */
export const permissionsIsAllow = async (type: Permissions, cb:Function) => {
  let bundleInfo: bundleManager.BundleInfo = await bundleManager.getBundleInfoForSelf(bundleFlags);
  let appInfo: bundleManager.ApplicationInfo = bundleInfo.appInfo;
  tokenID = appInfo.accessTokenId;
  console.log('tokenID', tokenID)
  try {
      atManager.checkAccessToken(tokenID, type).then((data) => {
      console.log(`${type} success, data->${JSON.stringify(data)}`);
      if (data === 0) { // 已授权
        cb()
      } else { // 未授权
        AlertDialog.show(
          {
            title: '温馨提示',
            message: '您还没有授权',
            autoCancel: false,
            alignment: DialogAlignment.Bottom,
            gridCount: 4,
            primaryButton: {
              value: '取消授权',
              action: () => {
                console.info('Callback when the first button is clicked')
                AlertDialog.show(
                  {
                    title: '温馨提示',
                    message: '必须要授权才能使用,是否前往应用进行授权',
                    autoCancel: false,
                    alignment: DialogAlignment.Bottom,
                    gridCount: 4,
                    primaryButton: {
                      value: '取消',
                      action: () => {
                        console.warn('用户再次取消授权')
                      }
                    },
                    secondaryButton: {
                      value: '前往授权',
                      action: () => {
                        let wantInfo = {
                          action: 'action.settings.app.info',
                          parameters: {
                            settingsParamBundleName: 'com.example.medicaltreatment' // 打开指定应用的详情页面
                          }
                        }
                        context.startAbility(wantInfo).then((data) => {
                          // ...
                          console.info('前往授权页面成功', JSON.stringify(data))
                        }).catch((err) => {
                          // ...
                          console.error('前往授权页面失败', JSON.stringify(err))
                        })
                      }
                    }
                  }
                )
              }
            },
            secondaryButton: {
              value: '确认授权',
              action: () => {
                atManager.requestPermissionsFromUser(context, [type]).then((data) => {
                  console.info("data:" + JSON.stringify(data));
                  console.info("data permissions:" + data.permissions);
                  console.info("data authResults:", JSON.stringify(data.authResults));
                  let length: number = data.authResults.length;
                  for (let i = 0; i < length; i++) {
                    if (data.authResults[i] === 0) {
                      // 用户授权,可以继续访问目标操作
                      cb()
                    } else {
                      // 用户拒绝授权,提示用户必须授权才能访问当前页面的功能,并引导用户到系统设置中打开相应的权限
                      return;
                    }
                  }
                }).catch((err) => {
                  console.info("data:" + JSON.stringify(err));
                })
              }
            },
            cancel: () => {
              console.info('Closed callbacks')
            }
          }
        )
      }
    }).catch((err) => {
      console.warn(`${type} fail, err->${JSON.stringify(err)}`);
    });
  } catch(err) {
    console.log(`catch err->${JSON.stringify(err)}`);
  }
}

效果:

4、选择图片

/**
 * 选择图片 单/多
 */
export const chooseImage = (cb: Function, number: number) => {
  try {
    let PhotoSelectOptions = new picker.PhotoSelectOptions();
    PhotoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE;
    PhotoSelectOptions.maxSelectNumber = number;
    let photoPicker = new picker.PhotoViewPicker();
    photoPicker.select(PhotoSelectOptions).then((PhotoSelectResult) => {
      console.info('PhotoViewPicker.select successfully, PhotoSelectResult uri: ' + JSON.stringify(PhotoSelectResult));
      console.log('PhotoSelectResult111', PhotoSelectResult)
      if (PhotoSelectResult && PhotoSelectResult.photoUris) {
        const imgList = PhotoSelectResult.photoUris;
        cb(imgList)
      }
    }).catch((err) => {
      console.error('PhotoViewPicker.select failed with err: ' + err);
    });
  } catch (err) {
    console.error('PhotoViewPicker failed with err: ' + err);
  }
}

选中的图片的路径是这样的:

5、将datashare的路径进行转换

为什么要转换: 看官方文档:文档中心

private onDealWithData = (fileUris: string[]) => {
    console.log('appLog:fileUris:', JSON.stringify(fileUris));
    this.showChooseImage = fileUris
    this.onUploadImageFileList = []
    const promises = fileUris.map(async item => await this.onPromiseTask(item));
    Promise.all(promises)
      .then(() => {
        console.log("All tasks completed. Proceed to the next step.");
        if (this.onUploadImageFileList.length > 0) {
          this.onUploadFile(this.onUploadImageFileList);
        } else {
          console.log('onUploadImageFileList的长度为0')
        }
        // 在这里执行下一步操作
      })
      .catch(error => {
        console.error("Error occurred:", error);
      });
  }
  private onPromiseTask = (v) => {
    return new Promise((resolve, reject) => {
      fs.open(v, fs.OpenMode.READ_ONLY).then((file) => { // READ_ONLY READ_WRITE
        const dateStr = (new Date().getTime()).toString()
        let newPath = context.cacheDir + `/${dateStr}.png`;
        fs.copyFile(file.fd, newPath).then(() => {
          console.info("applog:copy file succeed");
          let realUri = "internal://cache/"+newPath.split("cache/")[1];
          const obj = {
            filename: `${dateStr}.png`,
            name: "files",
            uri: realUri,
            type: "png"
          }
          this.onUploadImageFileList.push(obj);
          resolve(true)
        }).catch((err) => {
          console.info("applog:copy file failed with error message: " + err.message + ", error code: " + err.code);
        });
      }).catch((err) => {
        console.info("applog:open file failed with error message: " + err.message + ", error code: " + err.code);
      });
    })
  }

转换好之后的文件路径是这样的:

6、上传到服务器

/**
 * 上传图片
 */
export const uploadImageListOrSingle = async (files, cb:Function) => {
  const token = await PreferenceModel.getPreference('tokenInfo', 'token')
  const uploadConfigData = []
  console.info('转换之后的files', JSON.stringify(files))
  files.forEach(v => {
    const uploadConfig = {
      url: 'https://xxx.xxx.xxx/api/v1.0/oss/upload',
      header: {
        'Hwkj-Custom-Client': 'PlatformOfWeb',
        "Authorization": token ? 'Bearer ' + token : '',
      },
      method: "POST",
      files: [v],
      data: [{ name: "files", value: "files"}],
    }
    uploadConfigData.push(uploadConfig)
  })
  const promises = uploadConfigData.map(async item => await onGetImageUploadBackUrl(item));
  Promise.all(promises)
    .then((data) => {
      const showList = []
      data.forEach(v => {
        showList.push(v[0])
      })
      cb(showList)
    })
    .catch(error => {
      console.error("Error occurred1:", error);
    });
}
const onGetImageUploadBackUrl = (uploadConfig) => {
  let uploadTask;
  return new Promise((resolve, reject) => {
    try {
      request.uploadFile( getContext(this), uploadConfig).then((data) => {
        console.info('JSON.stringify(data)', JSON.stringify(data))
        uploadTask = data;
        let upProgressCallback = (data) => {
          console.info("data1111:" + JSON.stringify(data));
          resolve(data)
        };
        uploadTask.on('complete', upProgressCallback);
      }).catch((err) => {
        console.error('Failed to request the upload. Cause: ' + JSON.stringify(err));
      });
    } catch (err) {
      console.error('applog:', JSON.stringify(err));
      console.error('err.code : ' + err.code + ', err.message : ' + err.message);
    }
  })
}

6、显示选中的和上传接口返回的数据信息(效果图)

注意事项:

1、不知道为什么checkAccessToken永远走的.catch,不知道是代码原因还是本来这个api的原因。导致用户同意授权之后,下一次仍然是未授权。目前还未解决,还在等官方的回答(已解决 上方的代码已经解决了)

2、大家可以看到 当通过上传接口返回的数据并不是服务器返回的数据而是官方返回的图片信息

我已经对这个问题给官方说了:

“我们这个上传接口会返回一个图片的url 但是现在执行complete之后 返回的并不是服务器返回的数据 这种情况怎么办呢?”

官方的回答是:

7、完整代码

1、页面ui代码

import fs from '@ohos.file.fs';
import common from '@ohos.app.ability.common';
let context = getContext(this) as common.UIAbilityContext;
import { permissionsIsAllow, chooseImage, uploadImageListOrSingle } from '../../utils'
@Entry
@Component
struct AccountSettingIndex {
  @State showListData: any[] = []
  @State showChooseImage: any[] = []
  @State onUploadImageFileList: any[] = []
  private onChooseImage = () => {
    permissionsIsAllow('ohos.permission.WRITE_MEDIA', () => { // READ_MEDIA
      this.onChooseImageFin()
    })
  }
  private onChooseImageFin = () => {
    chooseImage((imgList) => {
      this.onDealWithData(imgList)
    }, 2)
  }
  private onDealWithData = (fileUris: string[]) => {
    console.log('appLog:fileUris:', JSON.stringify(fileUris));
    this.showChooseImage = fileUris
    this.onUploadImageFileList = []
    const promises = fileUris.map(async item => await this.onPromiseTask(item));
    Promise.all(promises)
      .then(() => {
        console.log("All tasks completed. Proceed to the next step.");
        if (this.onUploadImageFileList.length > 0) {
          this.onUploadFile(this.onUploadImageFileList);
        } else {
          console.log('onUploadImageFileList的长度为0')
        }
        // 在这里执行下一步操作
      })
      .catch(error => {
        console.error("Error occurred:", error);
      });
  }
  private onPromiseTask = (v) => {
    return new Promise((resolve, reject) => {
      fs.open(v, fs.OpenMode.READ_ONLY).then((file) => { // READ_ONLY READ_WRITE
        const dateStr = (new Date().getTime()).toString()
        let newPath = context.cacheDir + `/${dateStr}.png`;
        fs.copyFile(file.fd, newPath).then(() => {
          console.info("applog:copy file succeed");
          let realUri = "internal://cache/"+newPath.split("cache/")[1];
          const obj = {
            filename: `${dateStr}.png`,
            name: "files",
            uri: realUri,
            type: "png"
          }
          this.onUploadImageFileList.push(obj);
          resolve(true)
        }).catch((err) => {
          console.info("applog:copy file failed with error message: " + err.message + ", error code: " + err.code);
        });
      }).catch((err) => {
        console.info("applog:open file failed with error message: " + err.message + ", error code: " + err.code);
      });
    })
  }
  private onUploadFile = async (fileUri) => {
    uploadImageListOrSingle(fileUri, (data) => {
      console.log('获取到的url是', typeof data ,data)
      this.showListData = data
    })
  }
  build() {
    Row() {
      Column() {
        Button('选择图片28')
          .onClick(this.onChooseImage)
        // 显示选中的图片
        ForEach(this.showChooseImage, (item, idnex) => {
          Image(item).width(80).height(80).margin({bottom: 20})
        })
        // 为了方便展示选中的图片信息
        ForEach(this.showListData, (item, idnex) => {
          Text(item.path)
        })
      }
      .width('100%')
    }
    .height('100%')
  }
}

2、utils文件下面的三个方法

import abilityAccessCtrl, { Permissions } from '@ohos.abilityAccessCtrl';
let atManager = abilityAccessCtrl.createAtManager();
import bundleManager from '@ohos.bundle.bundleManager';
// let bundleFlags = bundleManager.BundleFlag.GET_BUNDLE_INFO_DEFAULT;
let bundleFlags = bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION;
// let tokenID = bundleFlags;
let tokenID
import common from '@ohos.app.ability.common';
import picker from '@ohos.file.picker';
import request from '@ohos.request';
let context = getContext(this) as common.UIAbilityContext;
/**
 * 对应用权限进行校验封装 我这边默认只能一个一个授权,多个授权自己封装
 */
export const permissionsIsAllow = async (type: Permissions, cb:Function) => {
  let bundleInfo: bundleManager.BundleInfo = await bundleManager.getBundleInfoForSelf(bundleFlags);
  let appInfo: bundleManager.ApplicationInfo = bundleInfo.appInfo;
  tokenID = appInfo.accessTokenId;
  console.log('tokenID', tokenID)
  try {
      atManager.checkAccessToken(tokenID, type).then((data) => {
      console.log(`${type} success, data->${JSON.stringify(data)}`);
      if (data === 0) { // 已授权
        cb()
      } else { // 未授权
        AlertDialog.show(
          {
            title: '温馨提示',
            message: '您还没有授权',
            autoCancel: false,
            alignment: DialogAlignment.Bottom,
            gridCount: 4,
            primaryButton: {
              value: '取消授权',
              action: () => {
                console.info('Callback when the first button is clicked')
                AlertDialog.show(
                  {
                    title: '温馨提示',
                    message: '必须要授权才能使用,是否前往应用进行授权',
                    autoCancel: false,
                    alignment: DialogAlignment.Bottom,
                    gridCount: 4,
                    primaryButton: {
                      value: '取消',
                      action: () => {
                        console.warn('用户再次取消授权')
                      }
                    },
                    secondaryButton: {
                      value: '前往授权',
                      action: () => {
                        let wantInfo = {
                          action: 'action.settings.app.info',
                          parameters: {
                            settingsParamBundleName: 'com.example.medicaltreatment' // 打开指定应用的详情页面
                          }
                        }
                        context.startAbility(wantInfo).then((data) => {
                          // ...
                          console.info('前往授权页面成功', JSON.stringify(data))
                        }).catch((err) => {
                          // ...
                          console.error('前往授权页面失败', JSON.stringify(err))
                        })
                      }
                    }
                  }
                )
              }
            },
            secondaryButton: {
              value: '确认授权',
              action: () => {
                atManager.requestPermissionsFromUser(context, [type]).then((data) => {
                  console.info("data:" + JSON.stringify(data));
                  console.info("data permissions:" + data.permissions);
                  console.info("data authResults:", JSON.stringify(data.authResults));
                  let length: number = data.authResults.length;
                  for (let i = 0; i < length; i++) {
                    if (data.authResults[i] === 0) {
                      // 用户授权,可以继续访问目标操作
                      cb()
                    } else {
                      // 用户拒绝授权,提示用户必须授权才能访问当前页面的功能,并引导用户到系统设置中打开相应的权限
                      return;
                    }
                  }
                }).catch((err) => {
                  console.info("data:" + JSON.stringify(err));
                })
              }
            },
            cancel: () => {
              console.info('Closed callbacks')
            }
          }
        )
      }
    }).catch((err) => {
      console.warn(`${type} fail, err->${JSON.stringify(err)}`);
    });
  } catch(err) {
    console.log(`catch err->${JSON.stringify(err)}`);
  }
}
/**
 * 选择图片 单/多
 */
export const chooseImage = (cb: Function, number: number) => {
  try {
    let PhotoSelectOptions = new picker.PhotoSelectOptions();
    PhotoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE;
    PhotoSelectOptions.maxSelectNumber = number;
    let photoPicker = new picker.PhotoViewPicker();
    photoPicker.select(PhotoSelectOptions).then((PhotoSelectResult) => {
      console.info('PhotoViewPicker.select successfully, PhotoSelectResult uri: ' + JSON.stringify(PhotoSelectResult));
      console.log('PhotoSelectResult111', PhotoSelectResult)
      if (PhotoSelectResult && PhotoSelectResult.photoUris) {
        const imgList = PhotoSelectResult.photoUris;
        cb(imgList)
      }
    }).catch((err) => {
      console.error('PhotoViewPicker.select failed with err: ' + err);
    });
  } catch (err) {
    console.error('PhotoViewPicker failed with err: ' + err);
  }
}
/**
 * 上传图片
 */
export const uploadImageListOrSingle = async (files, cb:Function) => {
  const token = await PreferenceModel.getPreference('tokenInfo', 'token')
  const uploadConfigData = []
  console.info('转换之后的files', JSON.stringify(files))
  files.forEach(v => {
    const uploadConfig = {
      url: 'https://xxx.xxx.com/api/v1.0/oss/upload',
      header: {
        'Hwkj-Custom-Client': 'PlatformOfWeb',
        "Authorization": token ? 'Bearer ' + token : '',
      },
      method: "POST",
      files: [v],
      data: [{ name: "files", value: "files"}],
    }
    uploadConfigData.push(uploadConfig)
  })
  const promises = uploadConfigData.map(async item => await onGetImageUploadBackUrl(item));
  Promise.all(promises)
    .then((data) => {
      const showList = []
      data.forEach(v => {
        showList.push(v[0])
      })
      cb(showList)
    })
    .catch(error => {
      console.error("Error occurred1:", error);
    });
}
const onGetImageUploadBackUrl = (uploadConfig) => {
  let uploadTask;
  return new Promise((resolve, reject) => {
    try {
      request.uploadFile( getContext(this), uploadConfig).then((data) => {
        console.info('JSON.stringify(data)', JSON.stringify(data))
        uploadTask = data;
        let upProgressCallback = (data) => {
          console.info("data1111:" + JSON.stringify(data));
          resolve(data)
        };
        uploadTask.on('complete', upProgressCallback);
      }).catch((err) => {
        console.error('Failed to request the upload. Cause: ' + JSON.stringify(err));
      });
    } catch (err) {
      console.error('applog:', JSON.stringify(err));
      console.error('err.code : ' + err.code + ', err.message : ' + err.message);
    }
  })
}

上一章:HarmonyOS自定义标题栏-CSDN博客

下一章:HarmonyOS文件下载以及消息通知-CSDN博客

  • 19
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 18
    评论
华为鸿蒙HarmonyOS开发整理资料汇总,共38份。 1学前必读:HarmonyOS学习资源主题分享 2学前必读:OpenHarmony-联盟生态资料合集 3-1.HarmonyOS概述:技术特性 3-2.HarmonyOS概述:开发工具与平台 3-3.HarmonyOS概述:系统安全 3-4.HarmonyOS概述:系统定义 3-5.HarmonyOS概述:下载与安装软件 3-6.HarmonyOS概述:应用开发基础知识 3-7.HarmonyOS概述:最全HarmonyOS文档和社区资源使用技巧 4-1.生态案例:【开发者说】重塑经典,如何在HarmonyOS手机上还原贪吃蛇游戏 4-2.生态案例:HarmonyOLabo涂鸦鸿蒙亲子版 4-3.生态案例:HarmonyOS分镜头APP案例 4-4.生态案例:HarmonyOS时光序历史学习案例 4-5.生态案例:HarmonyOS先行者说 宝宝巴士携手HarmonyOS共同打造儿童教育交互新体验 4-6.生态案例:HarmonyOS智能农场物联网连接实践 4-7.生态案例:分布式开发样例,带你玩转多设备 4-8.生态案例:华为分布式日历应用开发实践 5-1.【Codelab】HarmonyOS基于图像模块实现图库图片的四种常见操作 5-2.【CodeLab】手把手教你创建第一个手机“Hello World” 5-3.【Codelab】如此简单!一文带你学会15个HarmonyOS JS组件 5-4.【Codelab】懒人“看”书新法—鸿蒙语音播报,到底如何实现? 5-5.【Codelab】基于AI通用文字识别的图像搜索,这波操作亮了 5-6.【Codelab】开发样例概览 6-1.技术解读之HarmonyOS轻量JS开发框架与W3C标准差异分析 6-2.技术解读之HarmonyOS驱动加载过程分析 6-3.技术解读之HarmonyOS组件库使用实践 6-4.技术解读之华为架构师解读:HarmonyOS低时延高可靠消息传输原理 6-5.技术解读之解密HarmonyOS UI框架 6-6.技术解读之如何从OS框架层面实现应用服务功能解耦 7-1.常见问题之HarmonyOS元服务的设计与开发解析 7-2.常见问题之Java开发 7-3.常见问题之JS开发 7-4.常见问题之模拟器登录 7-5.常见问题之模拟器运行 7-6.常见问题之如何使用JsJava开发HarmonyOS UI 7-7.常见问题之应用配置 7-8.常见问题之预览器运行 8【视频合集】入门到进阶视频学习资料合集30+

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

多喜乐 长安宁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值