chooseImage: function() {
let that = this;
var sourceTypeArr = ['camera'];
var imageLimit = 6;
if (plus.storage.getItem("zhyl_album_open") == "1" && that.imageList.length < 2) {
sourceTypeArr.push("album");
}
if (plus.storage.getItem("zhyl_album_open") == "1") {
imageLimit = 8;
}
if (that.imageList.length < imageLimit && !that.isImageUploading) {
var Manifest;
if (plus.os.name == "Android") {
Manifest = plus.android.importClass("android.Manifest");
}
checkAndRequestPermission(plus.os.name == "Android" ? Manifest.permission.CAMERA : "camera",
function() {
that.isImageUploading = true;
uni.chooseImage({
count: 1,
sourceType: sourceTypeArr, //从相册选择
success: function(res) {
//压缩图片,大于200KB进行压缩
console.log("image size:" + res.tempFiles[0].size)
if (res.tempFiles[0].size > 200 * 1024) {
uni.compressImage({
src: res.tempFilePaths[0],
quality: plus.os.name == "iOS" ? 60 : 70,
width: "1024px",
success: result => {
console.log("compress image:" + result
.tempFilePath)
if (result.tempFilePath) {
that.uploadFile(result.tempFilePath,
true, 1);
} else {
uni.showToast({
icon: "none",
title: "压缩图片出现问题,请稍后重试"
})
}
},
fail: function(err) {
console.log("compress image fail:", JSON.stringify(err));
uni.showToast({
icon: "none",
title: "压缩图片失败,将以原图上传"
})
if (res.tempFilePaths[0]) {
that.uploadFile(res.tempFilePaths[0],
true, 2);
} else {
uni.showToast({
icon: "none",
title: "压缩图片获取图片失败"
})
}
}
})
} else {
console.log("image:" + res.tempFilePaths[0])
if (res.tempFilePaths[0]) {
that.uploadFile(res.tempFilePaths[0], true, 3);
} else {
uni.showToast({
icon: "none",
title: "获取图片失败"
})
}
}
},
fail: function() {
uni.showToast({
icon: "none",
title: "请重新拍摄"
})
that.isImageUploading = false;
}
});
})
} else if (that.imageList.length >= imageLimit) {
uni.showModal({
title: '提示',
content: "最多只能上传" + imageLimit + "张图片",
showCancel: false
});
}
},
//上传图片或上传录音文件
uploadFile: function(file, isImage, type) {
uni.showLoading({
mask: true
});
let that = this;
let md5Obj = this.$api.getMd5Str("zzlymobile");
let userToken = uni.getStorageSync('cg_user_token');
let signature = userToken.substring(0, 10) + md5Obj.md5.substring(0, 10) + userToken.substring(10);
var param = {
signature: signature,
tt: md5Obj.time,
api_token: Utils.getApiToken("zhyl/service/fillInfo"),
log_id: that.serviceDetail.log_id,
uid: plus.storage.getItem('cg_user_id'),
user_token: userToken,
mobile: plus.storage.getItem("cg_mobile"),
host: this.baseUrl
};
uni.uploadFile({
url: this.baseUrl + 'zhyl/service/fillInfo', //仅为示例,非真实的接口地址
filePath: file,
name: isImage ? 'img' : 'record',
formData: param,
success: function(result) {
uni.hideLoading();
if (result.data) {
let res = JSON.parse(result.data);
if (res.error_code == 0) {
if (isImage) {
if (res.data.new_image) {
that.imageList.push(res.data.new_image);
that.uploadPicAndPoint(1, res.data.new_image);
} else {
uni.getFileInfo({
filePath: file,
success: (fileRes) => {
uni.showToast({
icon: "none",
title: fileRes.error_msg || (
"读取文件成功,文件大小:" + fileRes
.size + " " + file + " " +
type)
})
},
fail: (error) => {
uni.showToast({
icon: "none",
title: (error.errMsg || "读取文件失败") +
file + " " + type
})
}
})
}
} else {
that.recordFilePath = that.audio.src;
that.isRecordUploaded = true;
}
} else if (res.error_code == 30001 || res.error_code == 201) {
plus.storage.removeItem("cg_user_token");
plus.storage.removeItem("lylz_user_token");
plus.storage.removeItem("lylz_user_id");
let link = '/hybrid/html/pages/login/login.html';
uni.navigateTo({
url: '/pages/WebviewTemplateNoHeader?link=' +
encodeURIComponent(link)
});
} else {
uni.showToast({
icon: "none",
title: res.error_msg || "图片或录音上传失败,请重新拍照上传"
})
if (!isImage) {
that.deleteAudioFile(true);
}
}
} else {
uni.showToast({
icon: "none",
title: res.error_msg || "图片或录音上传失败,请稍后重试"
})
if (!isImage) {
that.deleteAudioFile(true);
}
}
that.isImageUploading = false;
},
fail: function(err) {
uni.hideLoading();
console.log(JSON.stringify(err));
uni.showToast({
icon: "none",
title: "接口请求异常"
})
that.isImageUploading = false;
if (!isImage) {
that.deleteAudioFile(true);
}
}
});
},, - 发布:2026-03-30 14:33
- 更新:2026-03-30 14:34
- 阅读:56
产品分类: uniapp/App
PC开发环境操作系统: Mac
PC开发环境操作系统版本号: 15.7.4
HBuilderX类型: 正式
HBuilderX版本号: 5.05
手机系统: Android
手机系统版本号: Android 15
手机厂商: OPPO
手机机型: oppo
页面类型: vue
vue版本: vue2
打包方式: 云端
项目创建方式: HBuilderX
示例代码:
操作步骤:
不是必现的
不是必现的
预期结果:
用户上传图片成功
用户上传图片成功
实际结果:
用户上传图片失败
用户上传图片失败
bug描述:
前端通过uni.chooseImage拍照获取图片请求后端上传接口,后端无法读取到图片也没法上传,这个问题是偶发性的,不是必现的,每隔几天就有用户给我们反馈这个问题,
前端拍照上传的代码如图所示,用户手机上错误提示信息如图,根据图片提示信息可以看到uni.chooseImage(获取到了图片地址,但是获取不到图片内容
Ask小助手 - Ask社区AI助手
欢迎到专业群( uni-app 官方技术交流群 1 ) 咨询,群中有相关专业的管理员和群友。
作为DCloud官方人员,我对这个bug反馈进行如下评审:
一、反馈内容完整性评估
优点:
- 提供了较为完整的代码示例,包含
chooseImage、compressImage和uploadFile的完整调用链 - 环境信息较为齐全(HBuilderX 5.05、Android 15、Vue2、OPPO设备)
- 问题现象描述相对清晰(偶发性、后端获取为null、有临时路径但无内容)
不足与需补充:
- 缺少关键错误日志:虽然提到"用户手机上错误提示信息如图",但实际未提供截图或具体的错误码/错误信息
- 复现细节严重不足:仅说明"不是必现",但未提供发生频率(如1/10次还是1/1000次)、特定机型限制、网络环境(WiFi/4G/5G)、图片大小特征等
- 后端接收细节缺失:未说明后端接收到的请求体结构、Content-Type、文件字段是否为空对象还是完全缺失
- 调试信息不足:代码中虽有
uni.getFileInfo的调试逻辑,但未提供实际的调试输出结果
二、技术方案评审
代码层面发现的问题:
- 临时文件生命周期风险:根据知识库文档,"文件的临时路径,在应用本次启动期间可以正常使用",但在某些Android系统(特别是Android 15新权限体系)中,临时文件可能在压缩或上传过程中被系统清理
- 异步处理隐患:
compressImage成功后直接传递result.tempFilePath,但未验证该路径文件实际可访问性 - plus API混用:代码中混用
plus.storage(HTML5+ API)与uni API,在Android 15新权限模型下可能存在上下文权限不一致问题
建议验证点:
- 在
uploadFile前增加uni.getFileInfo验证,确认文件确实存在且size > 0 - 考虑在压缩后使用
uni.saveFile将临时文件转为持久化文件后再上传,避免系统回收风险
三、平台兼容性分析
根据知识库信息,需要特别关注:
- Android 15权限变更:Android 15(API 35)对存储权限和临时文件访问有更严格的限制,可能影响
uni.chooseImage返回的临时文件路径在后续操作中的可访问性 - Google Play照片权限政策:如应用需上架Google Play,受照片和视频权限政策影响,可能需要使用uni-chooseSystemMedia插件替代原生API
四、排查建议(基于知识库)
立即排查项:
-
在
uploadFile方法开头增加文件存在性验证:uni.getFileInfo({ filePath: file, success: (info) => { if(info.size === 0) { console.error('文件大小为0,可能已被清理'); } } })本助手使用国产模型,生成代码可能不准确。专业的代码生成需要全球顶尖的AI Coding模型。推荐使用uni-agent
-
替代方案:如问题持续,建议使用插件市场更稳定的方案:
- 通用上传方法插件
- 或考虑使用
uni-file-picker组件替代原生API
需要用户提供的信息:
- 具体的错误提示截图或日志(包括console.log输出)
- 发生问题时的具体场景(如:是否切换过应用、是否长时间停留在选择页面、图片大小范围)
- 后端接收到的HTTP请求体原始内容(确认文件字段是null还是空对象)
结论: 该反馈基本成立,但属于偶发性系统权限或资源回收问题,非必现bug。建议优先验证文件生命周期管理,并考虑使用持久化存储或成熟的上传插件替代当前实现。