HarmonyOS上传文件以及权限授权

搞了一天,差不多搞完了,记录下,目前官方还有一些问题 会在下一个next版本修复

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,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: '必须要授权才能使用,是否前往应用进行授权',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:',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,idnex) => {
          Text(item.path)
        })
      }
      .width('100%')
    }
    .height('100%')
  }
}

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

import abilityAccessCtrl,err->${JSON.stringify(err)}`);
    });
  } catch(err) {
    console.log(`catch err->${JSON.stringify(err)}`);
  }
}
/**
 * 选择图片 单/多
 */
export const chooseImage = (cb: Function,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,JSON.stringify(files))
  files.forEach(v => {
    const uploadConfig = {
      url: 'https://xxx.xxx.com/api/v1.0/oss/upload',err.message : ' + err.message);
    }
  })
}

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

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

原文地址:https://blog.csdn.net/qq_57952018/article/details/135404175

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


文章浏览阅读1.4k次。被@Observed装饰的类,可以被观察到属性的变化;子组件中@ObjectLink装饰器装饰的状态变量用于接收@Observed装饰的类的实例,和父组件中对应的状态变量建立双向数据绑定。这个实例可以是数组中的被@Observed装饰的项,或者是class object中是属性,这个属性同样也需要被@Observed装饰。单独使用@Observed是没有任何作用的,需要搭配@ObjectLink或者@Prop使用。_鸿蒙ark对象数组
文章浏览阅读1k次。Harmony OS_harmonyos创建数据库
文章浏览阅读1.1k次,点赞25次,收藏23次。自定义组件Header.ets页面(子组件)//自定义组件@Component//组件声明private title:ResourceStr//接收的参数build(){Row() {index.ets(父组件)//导入自定义组件@Entry@Componentbuild() {Column() {/*** 1. 自定义组件调用-----自定义组件------* 2. 在调用的组件上设置样式*/ShopTitle({ title: '商品列表' })
文章浏览阅读952次,点赞11次,收藏25次。ArkUI是一套构建分布式应用界面的声明式UI开发框架。它使用极简的UI信息语法、丰富的UI组件、以及实时界面预览工具,帮助您提升移动应用界面开发效率30%。您只需使用一套ArkTS API,就能在Android、iOS、鸿蒙多个平台上提供生动而流畅的用户界面体验。_支持ios 安卓 鸿蒙next的跨平台方案
文章浏览阅读735次。​错误: 找不到符号符号: 变量 Layout_list_item位置: 类 ResourceTable_错误: 找不到符号 符号: 变量 resourcetable 位置: 类 mainabilityslice
文章浏览阅读941次,点赞23次,收藏21次。harmony ARKTS base64 加解密_鸿蒙 鸿蒙加解密算法库
文章浏览阅读860次,点赞21次,收藏24次。使用自定义布局,实现子组件自动换行功能。图1自定义布局的使用效果创建自定义布局的类,并继承ComponentContainer,添加构造方法。//如需支持xml创建自定义布局,必须添加该构造方法实现ComponentContainer.EstimateSizeListener接口,在onEstimateSize方法中进行测量。......@Override//通知子组件进行测量//关联子组件的索引与其布局数据idx++) {//测量自身。_鸿蒙javaui
文章浏览阅读917次,点赞25次,收藏25次。这里需要注意的是,真机需要使用华为侧提供的测试机,测试机中会安装纯鸿蒙的系统镜像,能够体验到完整的鸿蒙系统功能,纯鸿蒙应用目前还不能完美地在 HarmonyOS 4.0 的商用机侧跑起来。当前,真机调试需要使用华为侧提供的测试机,测试机中会安装纯鸿蒙的系统镜像,能够体验到完整的鸿蒙系统功能,纯鸿蒙应用目前还不能完美地在 HarmonyOS 4.0 的商用机侧跑起来。另外,由于样式的解析是基于组件文件的纬度的,因此样式文件只能应用于被其引用的组件文件中,而不能跨文件应用,并且样式文件也只支持类选择器。_鸿蒙 小程序
文章浏览阅读876次,点赞17次,收藏4次。2. HarmonyOS应用开发DevEcoStudio准备-1HUAWEI DevEco Studio为运行在HarmonyOS和OpenHarmony系统上的应用和服务(以下简称应用/服务)提供一站式的开发平台。
文章浏览阅读811次。此对象主要映射JSON数组数据,比如服务器传的数据是这样的。_arkts json
文章浏览阅读429次。鸿蒙小游戏-数字华容道_华为鸿蒙手机自带小游戏
文章浏览阅读1.1k次,点赞24次,收藏19次。Ability是应用/服务所具备的能力的抽象,一个Module可以包含一个或多个Ability。
文章浏览阅读846次。本文带大家使用MQTT协议连接华为IoT平台,使用的是E53_IA1 智慧农业扩展板与 BearPi-HM_Nano 开发主板_mqtt 如何对接第三方iot平台
文章浏览阅读567次。HarmonyOS_arkts卡片
文章浏览阅读1k次,点赞19次,收藏20次。ArkTS开发鸿蒙OS连接mongoDB(后端node.js)2024最新教程
文章浏览阅读1.2k次,点赞23次,收藏15次。HarmonyOS与OpenHarmony(1)本质上的不同是:HarmonyOS是鸿蒙操作系统,而OpenHarmony则是从开源项目。这里可以联想一下Android,比如小米手机在Android开源系统的基础上开发了MIUI的手机操作系统,HarmonyOS就类似于MIUI,OpenHarmony类似Android基础底座。(2)HarmonyOS:是双框架,内聚了AOSP(Android Open Source Project )和OpenHarmony等。_鸿蒙模拟器开了怎么跑代码
文章浏览阅读1.1k次,点赞21次,收藏21次。鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之Navigation组件。
文章浏览阅读2k次。由于之前的哥们匆忙离职了,所以鸿蒙手表项目的新版本我临时接过来打包发布,基本上之前没有啥鸿蒙经验,但是一直是做Android开发的,在工作人员的指导下发现打包配置基本上和Android一样,所以这些都不是问题,这里记录一下使用过程中遇到的问题。!过程和遇到的问题基本上都讲解了,关机睡觉,打卡收工。_鸿蒙系统adb命令
文章浏览阅读7.3k次,点赞9次,收藏29次。39. 【多选题】_column和row容器中,设置子组件在主轴方向上的对齐格式
文章浏览阅读1.1k次,点赞13次,收藏24次。18.鸿蒙HarmonyOS App(JAVA)日期选择器-时间选择器点击button按钮显示月份与获取的时间。_harmonyos农历获取