uni-app 鸿蒙端请求图片上传请求不返回问题
·
问题描述
在使用 uni-app 开发跨平台应用时,发现一个奇怪的现象:
- 安卓端:图片上传接口正常工作,请求能发出,后端能接收并处理,响应也能正常返回
- 鸿蒙端:请求能正常发出,后端也能正常接收并处理,但响应始终收不到,最终导致请求超时
经过排查,发现响应内容其实很小(只有几个字段),并非大数据返回,但就是无法接收到。
问题原因
经过深入分析,发现根本原因是请求体大小导致的:
安卓端
Android 的网络框架(OkHttp)在发送请求时,会自动对请求体进行压缩。即使你发送的是一张几 MB 的原图,Android 也会自动压缩后再发送,实际发送的请求体可能只有几百 KB。(安卓端超过1MB也会出现)
鸿蒙端
鸿蒙端的网络请求不会自动压缩,会直接发送原始数据。当图片较大(超过 1MB)时:
- 请求能正常发出
- 后端能正常接收并处理
- 后端也正常返回响应
- 但客户端无法接收到响应
这是因为鸿蒙端在处理较大的请求体时,底层网络栈可能存在某些限制或 bug,导致响应无法正常返回。
问题复现
// 这段代码在安卓端正常,鸿蒙端会超时
uni.uploadFile({
url: "https://example.com/api/upload",
filePath: largeImagePath, // 一张 2MB+ 的图片
name: "file",
success: (res) => {
// 安卓端能进入这里
// 鸿蒙端永远不会进入,最终超时
console.log("上传成功", res);
},
fail: (err) => {
console.log("上传失败", err);
}
});
解决方案
方案一:压缩图片(推荐)
在上传前先压缩图片,减小请求体大小:
/**
* 压缩图片
* @param {string} filePath 原始图片路径
* @returns {Promise<string>} 压缩后的图片路径
*/
const compressImage = (filePath) => {
return new Promise((resolve, reject) => {
uni.compressImage({
src: filePath,
quality: 80, // 压缩质量 0-100
width: 1024, // 缩放宽度
success: (res) => {
console.log("压缩成功:", res.tempFilePath);
resolve(res.tempFilePath);
},
fail: (err) => {
console.error("压缩失败:", err);
// 压缩失败时返回原始路径
resolve(filePath);
}
});
});
};
/**
* 上传图片(先压缩再上传)
*/
const uploadImage = async (filePath) => {
uni.showLoading({ title: "上传中..." });
try {
// 先压缩图片
const compressedPath = await compressImage(filePath);
// 再上传
const res = await new Promise((resolve, reject) => {
uni.uploadFile({
url: "https://example.com/api/upload",
filePath: compressedPath,
name: "file",
success: (res) => resolve(res),
fail: (err) => reject(err)
});
});
console.log("上传成功:", res);
} catch (err) {
console.error("上传失败:", err);
} finally {
uni.hideLoading();
}
};
压缩参数建议:
| 参数 | 推荐值 | 说明 |
|---|---|---|
| quality | 60-80 | 压缩质量,越低文件越小,但清晰度下降 |
| width | 1024-1920 | 缩放宽度,身份证 OCR 1024 足够 |
方案二:分片上传
对于特别大的文件,可以考虑分片上传:
/**
* 分片上传大文件
*/
const uploadLargeFile = async (filePath) => {
const chunkSize = 512 * 1024; // 512KB 每片
const fs = uni.getFileSystemManager();
// 读取文件信息
const fileInfo = await new Promise((resolve, reject) => {
fs.getFileInfo({
filePath: filePath,
success: (res) => resolve(res),
fail: (err) => reject(err)
});
});
const totalSize = fileInfo.size;
const totalChunks = Math.ceil(totalSize / chunkSize);
for (let i = 0; i < totalChunks; i++) {
const start = i * chunkSize;
const end = Math.min(start + chunkSize, totalSize);
// 读取分片
const chunkData = await new Promise((resolve, reject) => {
fs.read_file({
filePath: filePath,
position: start,
length: end - start,
encoding: "base64",
success: (res) => resolve(res.data),
fail: (err) => reject(err)
});
});
// 上传分片
await new Promise((resolve, reject) => {
uni.request({
url: "https://example.com/api/uploadChunk",
method: "POST",
data: {
chunkIndex: i,
totalChunks: totalChunks,
data: chunkData
},
success: () => resolve(),
fail: (err) => reject(err)
});
});
console.log(`分片 ${i + 1}/${totalChunks} 上传完成`);
}
// 通知后端合并分片
await new Promise((resolve, reject) => {
uni.request({
url: "https://example.com/api/mergeChunks",
method: "POST",
data: { totalChunks },
success: () => resolve(),
fail: (err) => reject(err)
});
});
};
最佳实践
1. 封装统一的上传方法
// utils/upload.js
import { compressImage } from "./image";
/**
* 统一上传方法
* @param {string} url 接口地址
* @param {string} filePath 文件路径
* @param {object} options 配置项
* @returns {Promise<object>} 上传结果
*/
export const uploadFile = async (url, filePath, options = {}) => {
const {
name = "file",
formData = {},
needCompress = true,
compressQuality = 80
} = options;
// 显示加载
uni.showLoading({ title: "上传中..." });
try {
let uploadPath = filePath;
// 是否需要压缩
if (needCompress) {
uploadPath = await compressImage(filePath, compressQuality);
}
// 上传
const res = await new Promise((resolve, reject) => {
uni.uploadFile({
url,
filePath: uploadPath,
name,
formData,
header: {
"X-Access-Token": uni.getStorageSync("token")
},
success: (res) => {
if (res.statusCode === 200) {
resolve(JSON.parse(res.data));
} else {
reject(new Error(`请求失败: ${res.statusCode}`));
}
},
fail: (err) => reject(err)
});
});
return res;
} finally {
uni.hideLoading();
}
};
2. 使用示例
import { uploadFile } from "@/utils/upload";
// 上传身份证照片
const uploadIdCard = async (imagePath) => {
try {
const res = await uploadFile(
"/api/ocr/recognize",
imagePath,
{
name: "file",
needCompress: true,
compressQuality: 80
}
);
if (res.success) {
console.log("识别结果:", res.result);
}
} catch (err) {
console.error("上传失败:", err);
}
};
总结
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 鸿蒙端上传大图后无响应 | 请求体超过 1MB,鸿蒙网络栈无法正常处理响应 | 上传前压缩图片 |
| 安卓端正常 | Android 自动压缩请求体 | - |
| H5 端正常 | 浏览器有完善的网络处理 | - |
核心要点:
- 鸿蒙端不会自动压缩请求体
- 请求体超过 1MB 时会导致响应丢失
- 解决方案:上传前使用
uni.compressImage压缩图片 - 建议封装统一的上传方法,自动处理压缩逻辑
怀疑是uniapp框架的问题,但是并没有证据
作者:煊承岳
日期:2026-06-04
更多推荐




所有评论(0)