HarmonyOS鸿蒙Next中在真机上上传图片上传失败的问题

HarmonyOS鸿蒙Next中在真机上上传图片上传失败的问题 各位大佬,我想问一下,我在真机进行调试时,一到上传图片然后显示上传中就无法上传成功是什么原因,我有考虑过是我的代码问题,但是目前找不到哪里错误,我可以请求访问权限等。

13 回复

开发者您好,您可以参考上传图片的相关demo:如何从图库选择图片并上传到服务器HarmonyOS 发送请求上传图片的demo,您可以对比参考下您的代码是否需要修改。如果还是未能解决您的问题,麻烦您提供一下您的demo、相关的日志、您的设备版本和IDE版本,感谢您的支持和配合。

更多关于HarmonyOS鸿蒙Next中在真机上上传图片上传失败的问题的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


加个断点调试一下,或加几个log输出,看看执行到哪一步了。

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

背景知识:

楼主首先需要将你得错误的地方给出来,如日志,代码片段。这样大家都有方向对症找问题。上传文件大致如下示例代码

问题解决:

示例代码:

static async uploadFile(
        context: common.UIAbilityContext,
        filePath: string,
        uploadUrl: string
    ) {
        try {
            // 1. 读取文件
            const file = fileIo.openSync(filePath, fileIo.OpenMode.READ_ONLY);
            const stat = fileIo.statSync(filePath);
            const fileSize = stat.size;

            // 2. 创建 HTTP 请求
            const httpRequest = http.createHttp();

            // 3. 组装 form-data
            const formData: FormData = {
                // 文件字段名(和后端对应,一般是 file 或 upload)
                uri: `file://${filePath}`,
                name: 'file',
                fileName: filePath.split('/').pop(), // 文件名
                type: 'application/octet-stream',
                // 可加其他字段
                userId: "123456",
            };

            // 4. 发起上传
            const response = await httpRequest.request(uploadUrl, {
                method: http.RequestMethod.POST,
                connectTimeout: 60000,
                readTimeout: 60000,
                header: {
                    'Content-Type': 'multipart/form-data',
                },
                extraData: formData
            });

            // 5. 结果
            if (response.responseCode === 200) {
                console.info('上传成功:', response.result);
                return JSON.parse(response.result.toString());
            } else {
                console.error('上传失败 code:', response.responseCode);
                return null;
            }

        } catch (err) {
            const e = err as BusinessError;
            console.error('上传异常:', e.code, e.message);
            return null;
        }
    }


interface FormData {
    uri: string,
    name: string,
    fileName: string | undefined,
    type: string,
    userId: string,
}

下面是鸿蒙给出的 api 接口:

request.uploadFile多文件上传

这个问题信息有点少,但从 HarmonyOS 开发经验来看:

“图片能选中,显示上传中,但一直上传不成功”

大概率不是权限问题,而是以下几个常见原因。


1. 文件路径问题(最常见)

很多同学在模拟器正常,真机失败。

原因是:

photoAccessHelper.select()

或者

FilePicker

返回的是:

file://media/Photo/xxx

或者

datashare:///...

这样的 URI。

开发者直接把 URI 当成本地文件路径上传:

multipartFile.append(uri)

结果:

模拟器成功
真机失败

因为真机上传框架实际上拿不到文件内容。

建议打印:

console.info(uri)

确认获取到的是:

  • URI
  • 还是实际文件路径

如果是 URI,需要先通过:

fileUri.getUriFromPath()

或者

fs.open()

读取文件内容再上传。


2. 没有真正读取到文件内容

很多上传 SDK 要求:

ArrayBuffer

或者:

File

而开发者传的是:

字符串路径

例如:

{
  file: "/storage/Users/currentUser/..."
}

实际上服务器收到的是:

0KB文件

或者根本没收到文件。

建议检查:

fs.stat()

确认:

size > 0

3. HTTPS证书问题

真机比模拟器严格。

如果服务端:

  • 自签名证书
  • 过期证书
  • TLS配置异常

会出现:

一直上传中
最终失败

建议抓日志:

catch(err=>{
  console.error(JSON.stringify(err))
})

重点看:

SSL
TLS
CERT

相关报错。


4. 上传图片太大

HarmonyOS 上传大文件时:

例如:

10MB+
20MB+
HEIF原图

如果直接:

await httpRequest.uploadFile()

可能超时。

特别是:

华为手机拍摄的 HEIF

虽然体积不大:

但解码过程耗时

建议先打印:

fileSize

确认图片大小。


5. HEIF/HEIC格式问题

这个最近特别常见。

HarmonyOS相册拍照默认:

HEIF

服务端很多只支持:

jpg
jpeg
png

上传后:

接口返回失败

开发者误以为没上传成功。

建议打印:

mimeType

例如:

image/heif
image/heic

如果是:

image/heif

建议先转 JPEG。


6. Multipart格式不正确

很多后端要求:

Content-Type:
multipart/form-data

字段名固定:

file
image
avatar

而客户端传:

img
uploadFile

结果:

HTTP 200

但是:

服务端拿不到文件

建议用抓包工具确认。


7. 网络权限问题

确认:

{
  "name": "ohos.permission.INTERNET"
}

已经配置。

虽然你说其它接口能访问。

但还是建议确认。


8. 上传接口根本没发出去

建议增加日志:

console.info("开始上传")

upload()

console.info("上传结束")

以及:

onProgress
onHeaderReceive

看看停在哪一步。


9. 真机调试建议

直接看:

hdc shell hilog | grep -i http

或者:

hdc hilog

很多时候日志里直接会看到:

SSL handshake failed

File open failed

Permission denied

Upload timeout

Connection reset

比猜快得多。


如果是在 HarmonyOS NEXT(API 12/13/14/15/23)项目里,我建议优先排查这三个:

① 图片实际是不是 HEIF 格式

② 上传拿到的是 URI 还是真实文件

③ Multipart 文件字段是否正确

这三个问题占真机图片上传失败案例的 80% 以上。

如果能贴出:

  • 图片选择代码
  • 上传代码
  • 接口返回日志
  • hilog 错误日志

基本可以直接定位到具体原因。

  1. 你上传图片到哪里 是oss 还是那块 ,
  2. 去找一下 你上传的地址 是否有张图
  3. 上传图片是 给几个打印信息 , 题图 看下信息日志

几个可能原因 1.相册 URI 直接上传,没拷贝沙箱,这样会真机安全拦截,一直转圈

2.用了 http:// 接口,系统明文拦截,无报错。

3.没动态申请 READ_MEDIA ,有权限配置但实际没授权

最好贴一下你的具体报错。大家帮你分析

  1. 找到上传图片的代码,在上传成功地方打印相应日志(url:用于确定是哪个交易,请求结果,用于判断什么问题)
  2. 如果找不到,可以先通过 应用文件上传下载 去验证是否确实是自己的代码有问题
  3. 多打印日志和debug去确定报错信息

上传功能实现

  1. 导入需要的模块,示例中除去发起请求以及响应错误处理,还需用到CoreFileKit中的fileIo,需导入以下模块。

    import { rcp } from '@kit.RemoteCommunicationKit';
    import { BusinessError } from '@kit.BasicServicesKit';
    import { fileIo } from '@kit.CoreFileKit';
    
  2. 定义Session配置,定义一个名为SESSION_CONFIG的对象,用于配置请求会话。配置包括传输超时和安全设置,如远程验证和TLS版本。

    let SESSION_CONFIG: rcp.SessionConfiguration = {
      requestConfiguration: {
        transfer: {
          timeout: {
            connectMs: 6000
          }
        },
        security: {
          remoteValidation: 'skip',
          tlsOptions: {
            tlsVersion: 'TlsV1.3'
          }
        }
      }
    };
    
  3. 定义FdReadFile类,用于读取文件描述符(File Descriptor)指向的文件。read方法异步读取指定的ArrayBuffer缓冲区,并返回实际读取的字节数。

    class FdReadFile {
      readonly fd: number;
    
      constructor(fd: number) {
        this.fd = fd;
      }
    
      async read(buffer: ArrayBuffer): Promise<number> {
        return fileIo.read(this.fd, buffer);
      }
    }
    
  4. 创建会话并打开文件,以只读模式打开一个文件。fileIo.openSync方法返回一个文件描述符,如果打开失败,程序会打印错误信息并返回。

    const session = rcp.createSession(SESSION_CONFIG);
    const file = fileIo.openSync('/data/storage/el1/bundle/entry_test/resources/resfile/upload_file.txt',
      fileIo.OpenMode.READ_ONLY);
    if (!file) {
      console.error('fileIo.openSync failed');
      return;
    }
    
  5. 读取文件,创建一个FdReadFile实例,并分配一个缓冲区来读取文件。read方法异步执行,等待文件读取完成。

    const fdReadFile = new FdReadFile(file.fd);
    const buffer = new ArrayBuffer(1024 * 1024); // 假设文件大小为1MB
    await fdReadFile.read(buffer);
    
  6. 上传文件,使用会话的uploadFromFile方法将文件上传到指定的URL。UploadFromFile构造函数接受一个文件描述符读取文件。上传成功或失败时,会分别打印相应的信息。

    session.uploadFromFile('https://httpbin.org/anything', new rcp.UploadFromFile(fdReadFile))
    .then((response: rcp.Response) => {
      console.info(`Upload succeeded: ${response}`)
    })
    .catch((err: BusinessError) => {
      console.error(`Upload failed: error code is ${err.code}, error data is ${err.data}`)
    });
    
  7. 关闭文件和会话,释放资源。

    fileIo.closeSync(file.fd);
    session.close();
    

可以用ai助手检测一下。

我怀疑过是我的upload或者是请求写法的问题

在鸿蒙Next真机上传图片失败,常见原因:

  1. 未在module.json5中声明ohos.permission.INTERNETohos.permission.READ_MEDIA_IMAGES(API 12需动态申请)。
  2. 使用了fileUrifiles接口路径错误,应改用fileIo.open + Request方式。
  3. 真机调试需确保HAP签名与权限一致,且网络代理不拦截。检查日志中errCode进一步定位。

上传失败常见于以下几点,请逐一排查:

  1. 权限:真机调试需在module.json5中声明ohos.permission.INTERNET及读写存储权限(如ohos.permission.READ_MEDIAohos.permission.WRITE_MEDIA),并在代码中通过requestPermissionsFromUser动态申请,确保授权成功。

  2. 文件URI:通过系统选择器(如photoAccessHelper)获取的file://media格式URI,不能直接在网络请求中读取,需转换为应用沙箱路径。可使用fileIo.copyFile将文件复制到context.cacheDircontext.filesDir,再用该路径构造上传数据。

  3. 请求格式:使用@ohos.net.http上传时,必须正确构建multipart/form-dataextraData应为ArrayBufferstring,建议先通过fileIo.readSync读出文件ArrayBuffer,与表单字段一起拼接成multipart格式后提交。

  4. 网络与日志:检查真机网络连通性,抓取hilog日志过滤httpfileIo,观察是否有明确错误码(如权限拒绝、文件不存在、超时等)。

聚焦以上几点应能定位问题。

回到顶部