文章目录
场景
在日常开发中,常需实现用户头像更换、图片附件上传等功能。通过将图片转换为二进制流上传,可灵活适配不同后端接口需求,本文将详细介绍具体实现步骤及项目案例。
🟢一、步骤
在 UniApp 中上传二进制流图片,一般需要经过选择图片、读取为二进制数据、上传二进制数据到服务器这几个主要步骤
1.1、选择图片
使用 uni.chooseMedia 或 uni.chooseImage 方法让用户从相册或相机选择图片。
uni.chooseMedia(推荐,UniApp 3.1.0+ 支持)或 uni.chooseImage 调用系统媒体选择器。
uni.chooseMedia({
count: 1, // 选择图片的数量
mediaType: ['image'], // 只选择图片
success: (res) => {
const tempFilePath = res.tempFiles[0].tempFilePath;
// 后续可将 tempFilePath 用于读取二进制数据
},
fail: (err) => {
console.error('选择图片失败:', err);
}
});
1.2、 读取图片为二进制数据
使用 uni.getFileSystemManager().readFile 方法将选择的图片文件读取为ArrayBuffer 格式二进制数据
uni.getFileSystemManager().readFile({
filePath: tempFilePath,
success: (fileRes) => {
const binaryData = fileRes.data;
// 后续可将 binaryData 用于上传
},
fail: (err) => {
console.error('读取文件失败:', err);
}
});
1.3、上传二进制数据到服务器
通过 uni.request 发送二进制流,需注意请求头设置为 application/octet-stream。
uni.request({
url: 'https://xxx/upload', // 服务器上传接口地址
method: 'POST',
data: binaryData,
headers: {
'Content-Type': 'application/octet-stream'
},
success: (res) => {
if (res.statusCode === 200) {
console.log('上传成功:', res.data);
} else {
console.error('上传失败,状态码:', res.statusCode);
}
},
fail: (err) => {
console.error('上传失败:', err);
}
});
🟢二、项目案例
2.1、替换头像案例
uni.chooseMedia+uni.getFileSystemManager().readFile方案实现
页面结构
<view class="img-box" @click="onAvatatChange">
<image mode="aspectFill" class="img" :src="addformData.avatar"></image>
<view class="text">点击更换头像</view>
</view>
逻辑实现
// 修改头像
onAvatatChange() {
// 调用拍照/选择图片
uni.chooseMedia({
count: 1,
mediaType: ['image'],
success: (res) => {
console.log(res.tempFiles[0].tempFilePath)
const tempFilePath = res.tempFiles[0].tempFilePath;
// 读取图片文件为二进制数据
uni.getFileSystemManager().readFile({
filePath: tempFilePath,
success: (fileRes) => {
const binaryData = fileRes.data;
// 上传二进制数据到服务器
this.uploadBinaryData(binaryData, tempFilePath);
},
fail: (err) => {
console.error('读取文件失败:', err);
uni.showToast({
title: '读取文件失败',
icon: 'none'
});
}
});
}
})
},
uploadBinaryData(binaryData, tempFilePath) {
uni.uploadFile({
url: "https://xxxx/file/upload",
method: 'POST',
formData: binaryData,
filePath: tempFilePath,
name: "file",
header: {
'Authorization': uni.getStorageSync('token'),
'Content-Type': 'multipart/form-data'
},
success: (res) => {
let resut = JSON.parse(res.data)
if (resut.code == 200) {
this.addformData.avatar = resut.data.url
}
},
})
},
2.1、uView u-upload 上传封面
页面结构
<u-upload :fileList="fileList1" @afterRead="afterRead" @delete="deletePic" name="1" multiple
:maxCount="1"></u-upload>
// 删除图片
deletePic(event) {
console.log('删除', event)
this.api.FileDelete({
filePath: event.file.baseUrl
}).then(res => {
console.log('删除附件', res)
uni.showToast({
title: '删除成功',
icon: 'success',
duration: 1500
});
this[`fileList${event.name}`].splice(event.index, 1);
})
},
// 新增图片
async afterRead(event) {
// 当设置 multiple 为 true 时, file 为数组格式,否则为对象格式
let lists = [].concat(event.file);
let fileListLen = this[`fileList${event.name}`].length;
lists.map((item) => {
this[`fileList${event.name}`].push({
...item,
status: "uploading",
message: "上传中",
});
});
for (let i = 0; i < lists.length; i++) {
const result = await this.uploadFilePromise(lists[i]);
let item = this[`fileList${event.name}`][fileListLen];
this[`fileList${event.name}`].splice(
fileListLen,
1,
Object.assign(item, {
status: "success",
message: "",
url: result.url,
name: result.name,
baseUrl: result.baseUrl,
})
);
fileListLen++;
}
},
//上传图片
uploadFilePromise(file) {
console.log('file', file)
return new Promise((resolve, reject) => {
uni.getFileSystemManager().readFile({
filePath: file.url,
encoding: 'binary',
success: (readRes) => {
console.log('readRes', readRes)
console.log('文件内容长度:', readRes.data.length);
uni.uploadFile({
// url: "http://xxx/file/upload", // 仅为示例,非真实的接口地址
url: "https://xxxx/file/upload", // 仅为示例,非真实的接口地址
filePath: file.url,
name: "file",
// header: {
// 'Authorization': uni.getStorageSync('token'),
// 'Content-Type': 'application/octet-stream'
// },
header: {
// 添加请求头
'Authorization': uni.getStorageSync('token'),
// 可以添加其他请求头
'Content-Type': 'multipart/form-data'
},
formData: readRes,
// formData: {
// user: "test",
// },
success: async (res) => {
console.log('res文件上传', res)
setTimeout(() => {
resolve(JSON.parse(res.data).data);
uni.showToast({
title: '上传成功',
icon: 'success',
duration: 1500
});
}, 1000);
},
fail: (err) => {
reject(err);
}
});
},
fail: (err) => {
reject(err);
}
});
});
},
🟢 三、关键注意事项
3.1 二进制流与 FormData 区别
方式 | 适用场景 | 请求头设置 | 后端 |
---|---|---|---|
二进制流 | 纯文件字节流传输 | Content-Type: application/octet-stream | 直接读取请求体字节数据 |
FormData 封装 | 需附带其他字段(如业务参数) | Content-Type: multipart/form-data | 通过 file 字段解析文件 |
3.2 性能优化
- 压缩图片:上传前通过
uni.compressImage
压缩图片,减少传输数据量。 - 并行上传:使用
Promise.all
并行处理多图上传(需注意服务器接口是否支持)。 - 进度监听:通过
uni.uploadFile
的onProgressUpdate
回调显示上传进度条。
3.3 跨平台适配
- 小程序平台:
uni.chooseMedia
自动适配微信/支付宝/字节等小程序的图片选择接口。 - H5 平台:需确保服务器支持跨域请求(设置
Access-Control-Allow-Origin
)。 - App 平台:需在 manifest 中配置文件读写权限(
FS_WRITE_ACCESS
)。
✒️总结
UniApp 中上传二进制流图片的核心在于:
- 通过
uni.chooseMedia
或uni.chooseImage
获取图片临时路径; - 使用文件系统 API 读取为二进制数据;
- 根据后端要求选择
uni.request
(二进制流)或uni.uploadFile
(FormData 表单)上传。
实际开发中,建议优先使用 uni.chooseMedia
统一跨平台体验,并根据接口规范灵活设置请求头。对于复杂场景(如多图上传、进度显示),可结合组件库(如 uView)或自定义指令优化交互体验。
如果本文对你有帮助,欢迎点赞支持!如需进一步探讨前端或 UniApp 开发,可访问我的个人主页「前端初见」,期待与你共同成长!
如果这篇【文章】有帮助到你💖,希望可以给我点个赞👍,创作不易,如果有对前端端或者对python感兴趣的朋友,请多多关注💖💖💖,咱们一起探讨和努力!!!
👨🔧 个人主页 : 前端初见