HarmonyOS鸿蒙Next中request.uploadFile获取不到返回值,上传没效果(可有偿)

HarmonyOS鸿蒙Next中request.uploadFile获取不到返回值,上传没效果(可有偿)

上传文件,不知道上次成功还是失败,没有接口返回值找不到问题所在。大佬指教下,真没招了。
可以加联系方式V,可有偿:Rik-Q1129
代码稍微改动了下。使用的是api13

cke_82225.png

现在“uploadTask参数”打印出来是空对象,走的catch。

import { common } from "@kit.AbilityKit";
import fs from '@ohos.file.fs';
import { BusinessError, request } from "@kit.BasicServicesKit";
import { photoAccessHelper } from "@kit.MediaLibraryKit";
// 获取应用文件路径
let context = getContext(this) as common.UIAbilityContext;
let cacheDir = context.cacheDir;

/**
 * 从相册中获取文件
 */
export async function PickerPhoto(){
  // const options = new picker.PhotoSelectOptions() //api-12已弃用
  const options = new photoAccessHelper.PhotoSelectOptions
  //最多选择一个
  options.maxSelectNumber = 1;
  //文件类型
  options.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE
  //获取选中的图片地址
  const optionsView = new photoAccessHelper.PhotoViewPicker()
  let urls = await optionsView.select(options)

  // 没有选择的情况
  if(urls.photoUris.length<=0)return;
  let imgUrl = urls.photoUris[0]
  return imgUrl
}

/**
 * 将本地图片  文件拷贝到cache缓存目录
 */
export function copyImgToCache(photoImgPath:string){
  const file = fs.openSync(photoImgPath,fs.OpenMode.READ_ONLY)
  // 文件唯一标识
  let fileFD = file.fd
  let fileName = Date.now().toString() //避免文件重复,重新文件名
  const ext = 'jpg'
  let fullPath = cacheDir + "/" + fileName + "." + ext;
  fs.copyFileSync(fileFD,fullPath) //拷贝文件与路径

  return [`internal://cache/${fileName + '.' + ext}`,fileName + "." + ext]
}


/**
 * 封装上传任务为Promise
 */
function uploadTaskToPromise(uploadTask: request.UploadTask): Promise<void> {
  return new Promise((resolve, reject) => {
    uploadTask.on('complete', (taskStates: Array<request.TaskState>) => {
      console.log('completeTaskStates',JSON.stringify(taskStates))
      resolve()
    });
    uploadTask.on('fail', (taskStates: Array<request.TaskState>) => {
      console.log('failTaskStates',JSON.stringify(taskStates))
      reject(new Error(`上传失败: ${JSON.stringify(taskStates)}`));
    });
  });
}

/**
 * 上传文件
 */
interface OssData{
  key:string
  policy:string
  uuid:string
  credential:string
  timeStamp:string
  signature:string
  region:string
}

export async function FileUpload(
  bucketName: string,
  imgSrc: string | undefined,
  ossData: OssData
): Promise<void> {
  if (!imgSrc) {
    throw new Error('未选择图片');
  }

  const fileInfo = copyImgToCache(imgSrc);
  const fileUri = fileInfo[0];
  const fileName = fileInfo[1];

  // 注意:文件字段必须是最后一个参数!
  const formData: Array<request.RequestData> = [
    { name: 'key', value: ossData.key },
    { name: 'policy', value: ossData.policy },
    { name: 'x-amz-meta-uuid', value: ossData.uuid },
    { name: 'x-amz-server-side-encryption', value: 'AES256' },
    { name: 'X-Amz-Credential', value: ossData.credential },
    { name: 'X-Amz-Algorithm', value: 'AWS4-HMAC-SHA256' },
    { name: 'X-Amz-Date', value: ossData.timeStamp },
    { name: 'X-Amz-Signature', value: ossData.signature },
    { name: 'acl', value: 'public-read' },
  // 不要包含 Content-Type,由系统自动生成
  ];

  const uploadConfig: request.UploadConfig = {
    url: `https://${bucketName}.s3.${ossData.region}.amazonaws.com/`,
    header: {
      'Authorization': AppStorage.get('Token') || '',
      'lang': AppStorage.get('language') || 'en',
      // 不要手动设置 Content-Type
    },
    method: 'POST',
    files: [{
      filename: fileName,
      name: 'file',  // 必须是'file'
      uri: fileUri,
      type: 'image/jpeg'  // 根据实际类型调整
    }],
    data: formData
  };

  try {
    console.log('上传前uploadConfig',JSON.stringify(uploadConfig))

    const uploadTask = await request.uploadFile(context, uploadConfig);
    console.log('context--------',JSON.stringify(context));
    console.log('uploadTask参数',JSON.stringify(uploadTask));
    return await uploadTaskToPromise(uploadTask);
  } catch (err) {
    console.error('上传任务创建失败', JSON.stringify(err));
    throw new Error('文件上传失败');
  }
}

更多关于HarmonyOS鸿蒙Next中request.uploadFile获取不到返回值,上传没效果(可有偿)的实战教程也可以访问 https://www.itying.com/category-93-b0.html

11 回复

【背景知识】

  • JSON.stringify该方法将一个ArkTS对象或数组转换为JSON字符串,支持线性容器的转换,不支持非线性容器。
  • request.uploadFile创建并启动一个上传任务,使用Promise异步回调,支持HTTP协议。通过on(‘complete’|‘fail’)可获取任务上传时的成功信息或错误信息。
  • ohos.net.http (数据请求)提供HTTP数据请求能力。应用可以通过HTTP发起一个数据请求,支持常见的GET、POST、OPTIONS、HEAD、PUT、DELETE、TRACE、CONNECT方法。

【问题分析】

request.uploadFile返回UploadTask类,只有成员函数,JSON.stringify会忽略函数的打印,因此打印JSON.stringify(uploadTask)会打印出来为空,经过线下沟通,楼主实际是通过请求特定的API接口上传文件。而不是通过通用的http协议去上传文件。

【解决方案】

经过上述分析,使用httpRequest.request的请求方法上传文件。其请求方法为POST方法,因为要上传文件请求体字段为文件和字符串的混合,因此请求体content-type类型为multipart/form-data,multiFormDataList中字符串contentType设置为空字符串,文件按照文件类型设置,关键代码如下:

async function FileUpload(
  bucketName: string,
  imgSrc: string | undefined,
  cacheDir: string,
  ossData: OssData
): Promise<void> {
  if (!imgSrc) {
    throw new Error('未选择图片');
  }

  const cacheInfo = copyImgToCache(imgSrc, cacheDir) as CacheBuffer;
  const fileName = cacheInfo.fileName;
  const imgArrayBuffer = cacheInfo.imgArrayBuffer;

  let urlStr = `https://${bucketName}.s3.${ossData.region}.amazonaws.com/`
  const httpRequest = http.createHttp();
  let ContentType = 'multipart/form-data';
  let HttpRequestOptions:http.HttpRequestOptions = {
    method: http.RequestMethod.POST,
    header: {
      'Content-Type': ContentType,
    },
    connectTimeout: 60000, // 可选,默认为60000ms
    readTimeout: 60000, // 可选,默认为60000ms
  }
  if (ContentType==='multipart/form-data'){
    HttpRequestOptions.multiFormDataList = [
      {
        name: "acl",
        contentType: '', 
        data: 'public-read',
      },
      {
        name: "key",
        contentType: '',
        data: ossData.key,
      },
      {
        name: "policy",
        contentType: '', 
        data: ossData.policy,
      },
      {
        name: "Content-Type",
        contentType: '', 
        data: ContentType,
      },
      {
        name: "x-amz-meta-uuid",
        contentType: '', 
        data: ossData.uuid,
      },
      {
        name: "x-amz-server-side-encryption",
        contentType: '',
        data: 'AES256',
      },
      {
        name: "X-Amz-Credential", 
        contentType: '',
        data: ossData.credential,
      },
      {
        name: "X-Amz-Algorithm", 
        contentType: '', 
        data: 'AWS4-HMAC-SHA256',
      },
      {
        name: "X-Amz-Date", 
        contentType: '', 
        data: ossData.timeStamp,
      },
      {
        name: "X-Amz-Signature",
        contentType: '',
        data: ossData.signature,
      },
      {
      name: "file", 
      contentType: 'image/jpeg',
      data: imgArrayBuffer,
      remoteFileName: fileName
    }
    ]
  }
  console.log('HttpRequestOptions:',JSON.stringify(HttpRequestOptions))
  httpRequest.request(urlStr, HttpRequestOptions, (error: Error, data: http.HttpResponse) => {
    console.log('请求接口完成',JSON.stringify(data))
    if (!error) { //请求成功
      console.log('请求接口成功',JSON.stringify(data))
      httpRequest.destroy(); //成功或失败都要销毁请求
    } else {
      console.log(`请求失败 ${JSON.stringify(error)}`)
      httpRequest.destroy();
    }
  })
}

【总结】

如果在实际应用中服务端有指定的API请求接口,上传图片等文件应使用httpRequest.request方法使用POST请求资源,上传文件时其content-Type需要设置为multipart/form-data。

更多关于HarmonyOS鸿蒙Next中request.uploadFile获取不到返回值,上传没效果(可有偿)的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


问题处理了很久一直未得到解决,头皮都掉了一层。老哥加班加点,并且非常有耐心的帮忙一起寻找问题,一点点的解刨问题、分析报错等。期间加班加点的花了不少时间,一直不辞辛苦,直到问题解决。

非常感谢这位老哥,不仅是因为帮我解决了一个对我来讲比较难得难点,期间帮我分析问题,处理好后,那些地方有什么问题,为什么有这个问题都一一给我讲解,让我在技术层面也有进步。

上传文件的URI必须使用internal://cache/协议,但楼主代码中生成的路径未包含完整缓存目录路径。files配置中type字段应为完整的MIME类型,而非文件扩展名jpg,错误设置会导致服务器无法识别文件类型。

手动设置"Content-Type":"multipart/form-data;boundary=…"会覆盖系统自动生成的合法边界值,导致服务器报错not a multipart request,应移除手动设置。

// 修正后的files配置

let files: Array<request.File> = [

  {  

    filename: array,  

    name: 'file',  

    uri: array,  

    type: 'image/jpeg' // 修正为合法MIME类型

  }

]

// 修正后的header配置

header: {

  'Authorization': AppStorage.get('Token'),

  'lang': AppStorage.get('language')

  // 删除手动设置的Content-Type参数

},

一般上传文件都需要先把本地文件解析成字节流,然后再发送到服务器,需要从这里入手,看你代码都是直接传的文件名

找HarmonyOS工作还需要会Flutter的哦,有需要Flutter教程的可以学学大地老师的教程,很不错,B站免费学的哦:https://www.bilibili.com/video/BV1S4411E7LY/?p=17

看你手动设置了 Content-Type: "multipart/form-data;boundary=xxx"

这个应该是没有必要的,你可以去掉试试,参考 API

uploadTask.on('complete', (taskStates: Array<request.TaskState>) => {
  console.log('查询上传参数',JSON.stringify(taskStates))
  for (let i = 0; i < taskStates.length; i++) {
    console.info(`上传成功: ${JSON.stringify(taskStates[i])}`);
  }
});

这个方法执行了 JSON.stringify(taskStates)这个没有值。 console.info(上传成功: ${JSON.stringify(taskStates[i])});在这打印出来是undefind

uploadTask.on('progress',(progress) =>{
  console.info("progress headers:" + JSON.stringify(progress));
})

这个进度能看到返回值。

uploadTask.on('fail', (taskStates: Array<request.TaskState>) => {
  for (let i = 0; i < taskStates.length; i++) {
    console.info("upOnFail taskState:" + JSON.stringify(taskStates[i]));
  }
})

在HarmonyOS Next中,request.uploadFile获取不到返回值可能是由于以下原因:

  1. 未正确配置网络权限(ohos.permission.INTERNET)
  2. 上传地址或参数格式错误
  3. 回调函数未正确定义
  4. 文件路径无效或权限不足

检查代码示例:

request.uploadFile({
  url: 'https://example.com/upload',
  files: [{uri: 'file://...'}],
  success: (res) => {console.log(res)},
  fail: (err) => {console.error(err)}
});

从代码和描述来看,上传失败的主要问题可能是:

  1. 上下文(context)传递问题:确保传递给uploadFile的context是正确的UIAbilityContext实例。打印的context显示为空,这可能是根本原因。

  2. 文件URI格式问题:internal://cache/路径可能需要使用正确的URI格式,建议使用完整的file://路径。

  3. 权限检查:确保应用已申请ohos.permission.INTERNET和ohos.permission.READ_MEDIA权限。

  4. 服务器配置:检查上传URL和OSS参数是否正确,特别是region和bucketName的拼接。

建议先检查context是否正确获取,可以在调用uploadFile前打印context验证。同时检查网络请求是否被拦截,使用抓包工具确认请求是否发出。文件路径建议使用更明确的file://格式。

回到顶部