HarmonyOS 鸿蒙Next中通过uploadFile实现图片上传功能

HarmonyOS 鸿蒙Next中通过uploadFile实现图片上传功能 通过uploadFile实现图片上传功能,如何实现?

5 回复

从图库选择图片并上传到服务器,可参考以下示例代码:

import { request } from '@kit.BasicServicesKit';
import { fileIo as fs } from '@kit.CoreFileKit';
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { common } from '@kit.AbilityKit';
@Entry
@Component
struct Index {
  private openPhotoPicker() {
    // Obtain the application file path 
    let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
    let cacheDir = context.cacheDir;
    let photoPicker = new photoAccessHelper.PhotoViewPicker();
    photoPicker.select({
      MIMEType: photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE,
      maxSelectNumber: 1
    }, (_, result) => {
      if (result) {
        result.photoUris.forEach((uri) => {
          let file = fs.openSync(uri, fs.OpenMode.CREATE);
          // Copy the file to the cache directory
          fs.copyFileSync(file.fd, cacheDir + '/test.jpeg');
          this.uploadImage(['internal://cache/test.jpeg']);
        })
      }
    })
  }
  private uploadImage(paths: string[]) {
    let allFiles = Array<request.File>();
    let header = new Map<Object, string>();
    header.set('Content-Type', 'multipart/form-data');
    header.set('key2', 'value2');
    for (let i = 0; i < paths.length; i++) {
      allFiles[i] = {
        name: 'image' + i + '.jpeg',
        filename: 'image' + i + '.jpeg',
        uri: paths[i],
        type: 'image'
      }
    }
    let data: Array<request.RequestData> = [{ name: 'name', value: 'value' }];
    let uploadConfig: request.UploadConfig = {
      url: 'http://XXX&#34',
      header: header,
      method: 'POST',
      files: allFiles,
      data: data
    }
    try {
      request.uploadFile(this.getUIContext().getHostContext(), uploadConfig, (error, uploadTask) => {
        if (uploadTask) {
          uploadTask.on('progress', (uploadSize: number, totalSize: number) => {
            console.info('progress,uploadedSize:' + uploadSize + ',totalSize:' + totalSize);
          })
        } else {
          console.info('upload failure:' + error);
        }
      })
    } catch (error) {
      console.info('upload failure:' + error);
    }
  }
  build() {
    Column() {
      Button('选择图片上传')
        .width('100%')
        .onClick(() => {
          this.openPhotoPicker();
        })
    }
    .width('100%')
    .height('100%')
    .padding(16)
    .justifyContent(FlexAlign.End)
  }
}

记得申请网络权限哦!ohos.permission.INTERNET

更多关于HarmonyOS 鸿蒙Next中通过uploadFile实现图片上传功能的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


可以使用上传下载模块(ohos.request)的上传接口将本地文件上传。文件上传过程通过系统服务代理完成。在api12中,request.agent.create接口增加了设置代理地址的参数,支持设置自定义代理地址。

说明

· 当前上传应用文件功能。request.uploadFile方式仅支持上传应用缓存文件路径(cacheDir)下的文件,request.agent方式支持上传用户公共文件和应用缓存文件路径下的文件。 · 使用上传下载模块,需声明权限:ohos.permission.INTERNET。 · 上传下载模块不支持Charles、Fiddler等代理抓包工具。 · 上传下载模块接口目前暂不支持子线程调用场景,如TaskPool等。

// 方式一:request.uploadFile

// pages/xxx.ets

import { common } from ‘@kit.AbilityKit’; import fs from ‘@ohos.file.fs’; import { BusinessError, request } from ‘@kit.BasicServicesKit’;

@Entry @Component struct Index { build() { Row() { Column() { Button(“上传”).onClick(() => { // 获取应用文件路径 // 请在组件内获取context,确保this.getUIContext().getHostContext()返回结果为UIAbilityContext let context = this.getUIContext().getHostContext() as common.UIAbilityContext; let cacheDir = context.cacheDir; // 新建一个本地应用文件 try { let file = fs.openSync(cacheDir + ‘/test.txt’, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE); fs.writeSync(file.fd, ‘upload file test’); fs.closeSync(file); } catch (error) { let err: BusinessError = error as BusinessError; console.error(Invoke uploadFile failed, code is ${err.code}, message is ${err.message}); } // 上传任务配置项 let files: Array<request.File> = [ //uri前缀internal://cache 对应cacheDir目录 { filename: ‘test.txt’, name: ‘test’, uri: ‘internal://cache/test.txt’, type: ‘txt’ } ] let data: Array<request.RequestData> = [{ name: ‘name’, value: ‘value’ }]; let uploadConfig: request.UploadConfig = { url: ‘https://xxx’, header: { ‘key1’:‘value1’, ‘key2’:‘value2’ }, method: ‘POST’, files: files, data: data } // 将本地应用文件上传至网络服务器 try { request.uploadFile(context, uploadConfig) .then((uploadTask: request.UploadTask) => { uploadTask.on(‘complete’, (taskStates: Array<request.TaskState>) => { for (let i = 0; i < taskStates.length; i++) { console.info(upload complete taskState: ${JSON.stringify(taskStates[i])}); } }); }) .catch((err: BusinessError) => { console.error(Invoke uploadFile failed, code is ${err.code}, message is ${err.message}); }) } catch (error) { let err: BusinessError = error as BusinessError; console.error(Invoke uploadFile failed, code is ${err.code}, message is ${err.message}); } }) } } } }

// 方式二:request.agent

// pages/xxx.ets

import { common } from ‘@kit.AbilityKit’; import fs from ‘@ohos.file.fs’; import { BusinessError, request } from ‘@kit.BasicServicesKit’;

@Entry @Component struct Index { build() { Row() { Column() { Button(“上传”).onClick(() => { // 获取应用文件路径 // 请在组件内获取context,确保this.getUIContext().getHostContext()返回结果为UIAbilityContext let context = this.getUIContext().getHostContext() as common.UIAbilityContext; let cacheDir = context.cacheDir; // 新建一个本地应用文件 let file = fs.openSync(cacheDir + ‘/test.txt’, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE); fs.writeSync(file.fd, ‘upload file test’); fs.closeSync(file); let attachments: Array<request.agent.FormItem> = [{ name: “test”, value: [ { filename: “test.txt”, path: cacheDir + ‘/test.txt’, }, ] }]; let config: request.agent.Config = { action: request.agent.Action.UPLOAD, url: ‘http://xxx’, mode: request.agent.Mode.FOREGROUND, overwrite: true, method: “POST”, headers: { ‘key1’:‘value1’, ‘key2’:‘value2’ }, data: attachments }; request.agent.create(context, config).then((task: request.agent.Task) => { task.start((err: BusinessError) => { if (err) { console.error(Failed to start the upload task, Code: ${err.code} message: ${err.message}); return; } }); task.on(‘progress’, async(progress) => { console.warn(/Request upload status ${progress.state}, uploaded ${progress.processed}); }) task.on(‘completed’, async() => { console.warn(/Request upload completed); //该方法需用户管理任务生命周期,任务结束后调用remove释放task对象 request.agent.remove(task.tid); }) }).catch((err: BusinessError) => { console.error(Failed to create a upload task, Code: ${err.code}, message: ${err.message}); }); }) } } } }

文件上传是鸿蒙应用开发的核心场景(如头像上传、附件提交、图片分享),鸿蒙基于沙箱安全机制,要求上传文件必须先存入应用沙箱目录(cacheDir),再通过系统 API 提交。以下是图片上传、拍照上传、任意文件上传的完整实现,附企业级封装工具类。​

思路

1、使用Picker选择媒体库的图片与视频 (返回一个临时的图片地址 file:// 咱们可以直接预览 也可以继续向后走 拿到服务器地址再预览)

const photoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
...
 
const photoViewPicker = new photoAccessHelper.PhotoViewPicker();
photoViewPicker.select(photoSelectOptions).then().catch()

2、把图片拷贝到应用的缓存目录 (不支持直接通过相册本地路径请求接口,仅支持通过缓存目录上传 context.cacheDir)

- 2.1 通过 openSync 读取 媒体库文件
- 2.2 通过 fileIo.copyFileSync(file.fd媒体文件, copyFilePath沙箱文件)
 
细节:copyFilePath沙箱中文件生成规则必须唯一 
 
fileType
fileName   时间戳 + 随机数1000~9999 + . 后缀  (别忘了含后缀)
copyFilePath

3、上传文件 request.uploadFile到服务器

request.uploadFile(上下文信息, 匹配对象{
	请求方式method: 'POST'
	请求地址url: '',
	请求头
	header: {},
	请求参数
	files: [
			{  name: '参数名',  uri: "internal://cache/沙箱目录下的文件名含后缀",    type:'',filename:'文件名含后缀'   }
	]
})
.then(uploadTask => {
		// uploadTask.on(类型, 回调函数)
		// progress 订阅上传任务进度事件,使用callback异步回调。
		// headerReceive 服务器返回的
		// complete 订阅上传任务完成
		// fail 订阅上传任务失败
})

- 使用Picker选择媒体库的图片与视频

import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { fileIo } from '@kit.CoreFileKit';
import { BusinessError } from '@kit.BasicServicesKit';
 
@Entry
@Component
struct Index {
  @State localFilePath:string = ''
  build() {
    Column() {
      Button('拉起媒体库·选择照片').onClick(() => {
        const photoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
        photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE; // 过滤选择媒体文件类型为IMAGE
        photoSelectOptions.maxSelectNumber = 1; // 选择媒体文件的最大数目
        let uris: Array<string> = [];
        const photoViewPicker = new photoAccessHelper.PhotoViewPicker();
        photoViewPicker.select(photoSelectOptions).then((photoSelectResult: photoAccessHelper.PhotoSelectResult) => {
          uris = photoSelectResult.photoUris; // 数组 里面是一个个file 也就是本地媒体库图片地址  咱们可以直接预览
          this.localFilePath = photoSelectResult.photoUris[0]
          console.info('photoViewPicker.select to file succeed and uris are:' + uris);  // file://media/Photo/947/IMG_1742896185_814/IMG_814.jpg
        }).catch((err: BusinessError) => {
          console.error(`Invoke photoViewPicker.select failed, code is ${err.code}, message is ${err.message}`);
        })
      })
 
      Image(this.localFilePath).width('100%')
    }
  }
}

- 把图片拷贝到应用的沙箱目录

当前上传应用文件功能,不支持直接上传本地相册的文件,仅支持上传应用缓存文件路径(cacheDir)下的文件。

原因:考虑到安全,程序不能直接访问相册所有图片 仅仅支持用户选择后拷贝到自己的沙箱目录下 隔离性

// 把相册路径拷贝到缓存路径下(沙箱路径下)
const fileType = 'jpg'
const fileName = Date.now() + '_' + (Math.floor(Math.random() * (99999 - 11111)) + 11111) + '.' + fileType
const copyFilePath = getContext(this).cacheDir + '/' + fileName
 
 
const file = fileIo.openSync(this.uris[0], fileIo.OpenMode.READ_ONLY) // file://media/Photo/2/IMG_1735796348_001/00.jpg
fileIo.copyFileSync(file.fd, copyFilePath)
console.info('cacheDir: ' + copyFilePath);
  • 上传图片到服务器

准备好参数调用request.uploadFile()获得上传对象 uploader

给uploader对象注册progress事件,监听上传进度 requestRes.on(“progress”, (uploadedSize: number, totalSize: number)=>{})

request.uploadFile(上下文信息, 匹配对象{
	请求方式method: 'POST'
	请求地址url: '',
	请求头
	header: {},
	请求参数
	files: [
			{  name: '参数名',  uri: "internal://cache/沙箱目录下的文件名含后缀",    type:'',filename:'文件名含后缀'   }
	]
})
.then(uploadTask => {
		// uploadTask.on(类型, 回调函数)
		// progress 订阅上传任务进度事件,使用callback异步回调。
		// headerReceive 服务器返回的
		// complete 订阅上传任务完成
		// fail 订阅上传任务失败
})

- 示例代码

import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { fileIo } from '@kit.CoreFileKit';
import { BusinessError, request } from '@kit.BasicServicesKit';
 
 
export interface UploadFileType {
  database: string;
  url: string;
  preview: string;
}
 
export interface PostUploadFileResType {
  state: number;
  msg: string;
  data: UploadFileType;
}
 
 
@Entry
@Component
struct Index {
  @State localFilePath:string = ''
  build() {
    Column() {
      Button('选择的照片拷贝到沙箱').onClick(() => {
        // 一、拉起媒体库选择照片
        const photoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
        photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE; // 过滤选择媒体文件类型为IMAGE
        photoSelectOptions.maxSelectNumber = 1; // 选择媒体文件的最大数目
        let uris: Array<string> = [];
        const photoViewPicker = new photoAccessHelper.PhotoViewPicker();
        photoViewPicker.select(photoSelectOptions).then((photoSelectResult: photoAccessHelper.PhotoSelectResult) => {
          uris = photoSelectResult.photoUris; // 数组 里面是一个个file 也就是本地媒体库图片地址  咱们可以直接预览
          this.localFilePath = photoSelectResult.photoUris[0]
          console.info('photoViewPicker.select to file succeed and uris are:' + uris);  // file://media/Photo/947/IMG_1742896185_814/IMG_814.jpg
 
          // 二、拷贝到沙箱目录
 
          const fileType = 'jpg'
          const fileName = Date.now() + '_' + Math.floor(  Math.random() * (9999-1000+1)+1000  ) + '.' + fileType
          const copyFilePath = getContext(this).cacheDir + '/' + fileName
          // - 2.1 通过 openSync 读取 媒体库文件
          const file = fileIo.openSync(photoSelectResult.photoUris[0], fileIo.OpenMode.READ_ONLY)
          // - 2.2 通过 fileIo.copyFileSync(file.fd, copyFilePath)
          // fileIo.copyFileSync(原文件唯一标识, 沙箱目录及文件名)
          fileIo.copyFileSync(file.fd, copyFilePath)
          // 细节:copyFilePath沙箱中文件生成规则必须唯一
          // fileType
          // fileName   时间戳 + 随机数1000~9999 + . 后缀
          // copyFilePath
          console.log('最终沙箱目录:', copyFilePath)
 
          // 三、上传图片到服务器
          request.uploadFile(getContext(this), {
            method: 'POST',
            url: 'http://123.56.141.187:8001/upload/create',
            header: {},
            files: [
              {name: 'file', uri:'internal://cache/'+fileName,  type: fileType,filename:fileName }
            ],
            data: []
          })
            .then((uploadTask) => {
              uploadTask.on('progress', (uploadedSize: number, totalSize: number) => {
                console.info("upload totalSize:" + totalSize + "  uploadedSize:" + uploadedSize);
              })
              uploadTask.on('headerReceive', (headers:ESObject) => {
                const serverData: PostUploadFileResType = JSON.parse(headers?.body)
                console.log('服务器数据:', serverData.state)
                console.log('服务器数据:', serverData.msg)
                console.log('服务器数据:', serverData.data.database)  // 图片名
                console.log('服务器数据:', serverData.data.preview)   // 完整网址+图片
              })
              uploadTask.on('complete', () => {
                console.log('上传完成')
              })
              uploadTask.on('fail', () => {
                console.log('上传失败')
              })
            })
          // 三、上传图片到服务器 end
        }).catch((err: BusinessError) => {
          console.error(`Invoke photoViewPicker.select failed, code is ${err.code}, message is ${err.message}`);
        })
      })
 
      Image(this.localFilePath).width('100%')
    }
  }
}

在HarmonyOS Next中,通过uploadFile实现图片上传功能,主要使用@ohos.request模块的uploadFile接口。首先,使用@ohos.file.fs模块获取图片文件的URI。然后,配置上传参数,包括URL、文件路径、请求头等。调用uploadFile方法执行上传,并通过回调函数处理上传进度和结果。上传成功后,服务器会返回响应数据。注意,此功能需要网络权限,需在module.json5中声明ohos.permission.INTERNET

在HarmonyOS Next中,可以使用@ohos.request模块的uploadFile接口实现图片上传。以下是核心实现步骤:

  1. 获取文件对象:使用@ohos.file.fs@ohos.file.picker选择图片文件,获取URI。

  2. 配置上传参数:构建uploadFile所需的UploadFileOptions对象,包含:

    • url:服务器地址
    • files:文件数组(每个文件需包含filenamenameuri
    • data:附加表单数据(可选)
  3. 调用上传接口:使用uploadFile方法并处理回调。

示例代码片段:

import { uploadFile } from '@ohos.request';
import { fileUri } from '@ohos.file.picker'; // 实际需通过文件选择器获取

let options = {
  url: 'https://example.com/upload',
  files: [
    {
      filename: 'image.jpg',
      name: 'file',
      uri: fileUri // 替换为实际文件URI
    }
  ],
  data: [
    {
      name: 'key',
      value: 'value'
    }
  ]
};

uploadFile(options, (err, data) => {
  if (err) {
    console.error('Upload failed:', err);
  } else {
    console.log('Upload response:', data);
  }
});

注意事项:

  • 需在module.json5中声明网络权限:ohos.permission.INTERNET
  • 文件URI需通过系统文件选择器获取,直接使用路径字符串可能无效
  • 服务器需支持对应的接收格式

此方法适用于单张或多张图片上传,实际开发中需根据服务器接口调整参数。

回到顶部