uniapp 上传压缩图片
项目是用uniapp开发的,当时只是做App端,后来项目扩展到H5端, uniapp框架可以跨平台所以移动端和H5使用的是一套代码
上传头像的时候要求图片的大小在2MB一下,所以要压缩图片,App端当时使用的是uni.compressImage(OBJECT)压缩的(详情见: uniapp.dcloud.io/api/media/i… 但是H5不兼容;
先搞定H5的压缩吧!网上一搜一大把
/**
* H5压缩
* @param {Object} imgSrc 图片url
* @param {Object} callback 回调设置返回值
* */
export function compressH5(fileItem) {
return new Promise((resolve, reject) => {
let file_size = fileItem.size
let file_path = fileItem.path
let file_name = fileItem.name
let max_size = 1020 * 1020 * 1 // 2MB的图片
if (max_size > file_size) { // 判断是否需要压缩, 没有超过限制
resolve(fileItem)
} else {
let img = new Image();
img.src = file_path;
img.onload = function() {
var quality = max_size / file_size
console.log('quality=', quality)
var that = this;
var h = that.height * (quality / 1); // 默认按比例压缩
var w = that.width * (quality / 1);
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
var anw = document.createAttribute("width");
anw.nodeValue = w;
var anh = document.createAttribute("height");
anh.nodeValue = h;
canvas.setAttributeNode(anw);
canvas.setAttributeNode(anh);
ctx.drawImage(that, 0, 0, w, h); //压缩比例
// base64
var base64 = canvas.toDataURL('image/jpeg', quality);
canvas = null;
// file对象
var file = base64ToFile(base64, "file_" + Date.parse(new Date()) + ".jpg");
resolve(file)
}
}
})
}
/**
* base转File对象
* @param {Object} base64 base64地址
* */
export function base64ToFile(base64, filename) {
let arr = base64.split(',');
console.log(arr)
let mime = arr[0].match(/:(.*?);/)[1];
let bstr = atob(arr[1]);
let n = bstr.length;
let u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new File([u8arr], filename, {
type: mime
});
}
通过压缩获取到File的对象,如果直接使用之前上传图片的uni.uploadFile(OBJECT)(详情见:uniapp.dcloud.io/api/request… 会上传不成功,具体的问题不太清楚好像是file的path的问题,后端的同事不想动他的代码,只能自己动手了,直接写原生的ajax上传
function fileUpload(i) {
console.log('fileUpload ---->', requestInfo)
let item = requestInfo.files[i];
let fileData = {
fileIndex: i,
files: requestInfo.files,
...item
};
var formdata = new FormData();
formdata.append("file", item);
var xhr = new XMLHttpRequest();
xhr.upload.addEventListener("progress", function(e) {
var percentComplete = Math.round(e.loaded * 100 / e.total);
requestInfo.onProgressUpdate && requestInfo.onProgressUpdate(Object.assign({},
fileData, percentComplete.toString() + '%'));
});
xhr.addEventListener("load", function(e) {
requestInfo.uploadComplete && requestInfo.uploadComplete(Object.assign({}, fileData,
xhr.responseText));
});
xhr.addEventListener("error", function(e) {
requestInfo.uploadError && requestInfo.uploadError(Object.assign({}, fileData, e));
});
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
requestInfo.onEachUpdate && requestInfo.onEachUpdate({
data: xhr.responseText,
...fileData
});
fileList.push(xhr.responseText);
if (len <= i) {
resolve(fileList);
} else {
fileUpload(i + 1);
}
} else if (xhr.readyState === 4) {
reject('err');
}
}
xhr.open("post", requestInfo.url, true);
xhr.setRequestHeader('platform', requestInfo.header.platform);
xhr.setRequestHeader('Access-Token', requestInfo.header['Access-Token']);
xhr.send(formdata);
}
问题解决
当拿到回调图片是,区分平台,App端的走app处理,H5端的走h5的处理方式
// 选择图片
onAvatarChange() {
const app = this
// 选择图片
uni.chooseImage({
count: 1,
sizeType: ['compressed'],//可以指定是原图还是压缩图,默认二者都有['original','compressed'],
sourceType: ['album', 'camera'], //可以指定来源是相册还是相机['album', 'camera'],默认二者都有
success({
tempFiles // bol tempFiles tempFilePaths
}) {
app.imageList = []
// #ifdef APP-PLUS
compressImg(tempFiles[0]).then(file => {
app.imageList.push(file)
app.uploadFile() // 上传图片
}).catch(err => {
console.log('catch', err)
})
// #endif
// #ifdef H5
compressH5(tempFiles[0]).then(file => {
console.log('file 222 = ', file)
app.imageList.push(file)
app.uploadFileH5() // 上传图片
}).catch(err => {
console.log('catch', err)
})
// #endif
}
});
}
其他的问题
批量上传可以递归,或者队列。
最好不要for循环同时处理,很多是异步处理,for循环等于是一起发送处理操作,对本机或者服务器产生压力。
多使用Promise控制。
其他思路
可以将压缩后的base64数据通过uni.uploadFile(OBJECT)的附加参数上传给服务端,可能需要服务端的同事配合了