根据Uview官方文档所说,若是小程序和app项目,上传使用压缩图片,可以配置u-upload组件的sizeType属性为'compress',但这对H5是无效的。在实际开发中,图片压缩上传经常是一个必要的需求,所以本篇文章主要讲的是在H5项目中如何使用Uview的upload组件进行压缩图片
tool.js
/**
* 图片压缩
* imgSrc 地址
* scale 压缩质量 0-1
* type 文件类型
*/
const compressImg = (imgSrc, scale, type, callback) => {
// uni.$u.toast('压缩中')
var img = new Image();
img.src = imgSrc;
img.onload = function() {
var that = this;
var h = (img.height * scale).toFixed(0); // 默认按质量比例压缩
var w = (img.width * scale).toFixed(0);
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
var width = document.createAttribute("width");
width.nodeValue = w;
var height = document.createAttribute("height");
height.nodeValue = h;
canvas.setAttributeNode(width);
canvas.setAttributeNode(height);
ctx.drawImage(that, 0, 0, w, h);
var base64 = canvas.toDataURL('image/jpeg', scale); //压缩比例
canvas = null;
if (type == 'base64') {
let data = {
size: getBase64Size(base64),
type: type,
source: base64
}
callback(base64);
} else {
let blob = base64ToBlob(base64);
console.log('压缩后的大小', blob.size);
const blobUrl = window.URL.createObjectURL(blob); //blob地址
blob.source = blobUrl
callback(blob);
}
}
};
/**base转Blob */
const base64ToBlob = (base64) => {
var arr = base64.split(','),
mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], {
type: mime
});
};
/**获取base64的文件大小 */
const getBase64Size = () => {
let size = 0;
if (base64Str) { // 获取base64图片byte大小
const equalIndex = base64Str.indexOf('='); // 获取=号下标
if (equalIndex > 0) {
const str = base64Str.substring(0, equalIndex); // 去除=号
const strLength = str.length;
const fileLength = strLength - (strLength / 8) * 2; // 真实的图片byte大小
size = Math.floor(fileLength); // 向下取整
} else {
const strLength = base64Str.length;
const fileLength = strLength - (strLength / 8) * 2;
size = Math.floor(fileLength); // 向下取整
}
} else {
size = null;
}
return size
};
在upload-image.vue中使用
/** 压缩图片*/
compressImg(source, compressionRatio) {
let that = this;
return new Promise((resolve, reject) => {
that.$.compressImg(source.url, compressionRatio, source.type, compressRes => {
resolve(compressRes);
})
}).then((res) => {
source.size = res.size
// window.URL.revokeObjectURL(source.url) // 删除被压缩的缓存文件,这里注意,如果是相册选择上传,可能会删除相册的图片
source.url = res.source
source.thumb = res.source
return source
}).catch(err => {
console.log('图片压缩失败', err)
})
},
完整示例代码,以及我碰到的问题
<template>
<view class="actual-download">
<top-bar title="回单影像上传"></top-bar>
<view class="main page-content">
<view class="scanCode" v-if="showScan">
<!-- <mumu-get-qrcode @close="showScan=false" @success='getScan' @error="err"></mumu-get-qrcode> -->
<mumu-one-code @close="showScan=false" @success='getScan' definition :readers='["code_128_reader"]'></mumu-one-code>
</view>
<view class="main-top">
<u--input placeholder="请输入内容" border="surround" v-model="value"></u--input>
<uni-icons @click="showScan=true" size="28" type="scan" color="#6BA8F8"></uni-icons>
</view>
<view>
<u-upload :fileList="fileList1" @afterRead="afterRead" @delete="deletePic" name="1" multiple
:maxCount="10"></u-upload>
</view>
</view>
<bottom-bar rightBtnText="完成" @clickRight="rightBtnClick" :showLeftBtn="false"></bottom-bar>
</view>
</template>
<script>
import {
receiptPic
} from '@/common/api.js'
import compressImg from '@/util/compressImg.js'
export default {
data() {
return {
fileList1: [],
showScan: false,
value: '',
fileMaxSize: 2 * 1024 * 1024, // 超出2M开启压缩
maxSize:20 * 1024 * 1024, //图片最大不能超过20M
fileMinSize: 5 * 1024 // 最小为5KB
};
},
methods: {
err() {
},
getScan(val) {
this.value = val
this.showScan = false
},
// 删除图片
deletePic(event) {
this[`fileList${event.name}`].splice(event.index, 1)
},
/** 压缩图片*/
compressImg(source, compressionRatio) {
console.log('压缩图片', source)
return new Promise((resolve, reject) => {
compressImg(source.url, compressionRatio, source.type, source.name, compressRes => {
resolve(compressRes);
})
}).then((res) => {
source.size = res.blob.size
// window.URL.revokeObjectURL(source.url) // 删除被压缩的缓存文件,这里注意,如果是相册选择上传,可能会删除相册的图片
source.url = res.blob.source
source.thumb = res.blob.source
source.file = res.file
return source
}).catch(err => {
console.log('图片压缩失败', err)
})
},
/**获取文件大小倍数,生成质量比*/
getCompressionRatio(fileSize) {
const multiple = (fileSize / this.fileMaxSize).toFixed(2);
let compressionRatio = 1;
if (multiple > 5) {
compressionRatio = 0.5
} else if (multiple > 4) {
compressionRatio = 0.6
} else if (multiple > 3) {
compressionRatio = 0.7
} else if (multiple > 2) {
compressionRatio = 0.8
} else if (multiple > 1) {
compressionRatio = 0.9
} else {
compressionRatio = 2
}
return compressionRatio;
},
// 新增图片
async afterRead(event) {
// 当设置 multiple 为 true 时, file 为数组格式,否则为对象格式
let lists = [].concat(event.file)
let fileListLen = this[`fileList${event.name}`].length
for (let index in lists) {
const item = lists[index];
const fileSize = item.size;
const fileName = item.name ?? '';
console.log('文件大小', fileSize);
if (fileSize > this.fileMaxSize) {
const compressionRatio = this.getCompressionRatio(fileSize);
if (compressionRatio > 1) {
uni.$u.toast('文件' + fileName + '大于2M')
return false
}
// 超出1M自动压缩图片
await this.compressImg(item, compressionRatio)
if (item.size > this.maxSize) {
uni.$u.toast('文件' + fileName + '压缩后超出20M')
return false
}
}
if (item.size < this.fileMinSize) {
uni.$u.toast('文件' + fileName + '不能小于5KB')
return false
}
}
lists.map((item) => {
this[`fileList${event.name}`].push({
...item,
status: 'uploading',
message: '上传中'
})
})
for (let i = 0; i < lists.length; i++) {
const flag = lists[i]?.file ? 'file' : 'filePath'
const result = await this.uploadFilePromise(flag, flag === 'file' ? lists[i].file : lists[i].url)
let item = this[`fileList${event.name}`][fileListLen]
this[`fileList${event.name}`].splice(fileListLen, 1, Object.assign(item, {
status: 'success',
message: '',
url: lists[i].url,
res: result
}))
fileListLen++
}
},
uploadFilePromise(flag,url) {
console.log(uni.getStorageSync('lifeData'))
let token = uni.getStorageSync('lifeData')
return new Promise((resolve, reject) => {
let a = uni.uploadFile({
url: `/mp/file/notice/upload.do?token=${token.token}`,
[flag]: url,
// filePath:url
// file:url,
name: 'uploadfile',
formData: {},
success: (res) => {
setTimeout(() => {
let obj = JSON.parse(res.data)
obj = obj.path
obj = obj[0]
obj = obj[0]
obj = obj.split('|')
obj = obj[1]
// obj = window.location.protocol + window.location.hostname + obj
resolve(obj)
}, 1000)
}
});
})
},
async rightBtnClick() {
if (!this.value) {
uni.$u.toast('请填写运单号')
return
}
for (let i = 0; i < this.fileList1.length; i++) {
if (this.fileList1[i].status != 'success') {
uni.$u.toast('图片上传中,请稍后')
return
}
}
let arr = []
for (let i = 0; i < this.fileList1.length; i++) {
arr.push({
picName: this.fileList1[i].name,
picAddress: this.fileList1[i].res
})
}
const res = await receiptPic({
shippingCode: this.value,
picRecordDTOList: arr
})
if (res?.result == '1') {
uni.$u.toast(res.msg);
this.fileList1=[]
this.value=''
}
}
}
}
</script>
问题是大于2M后的图片上传后,服务器识别不了文件类型.一开始我在uni.uploadFile使用的是filePath,后面改成大于2M使用file上传,优化了下tools.js的代码.返回多了个file
const compressImg = (imgSrc, scale, type,fileName,callback) => {
// uni.$u.toast('压缩中')
var img = new Image();
img.src = imgSrc;
img.onload = function() {
var that = this;
var h = (img.height * scale).toFixed(0); // 默认按质量比例压缩
var w = (img.width * scale).toFixed(0);
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
var width = document.createAttribute("width");
width.nodeValue = w;
var height = document.createAttribute("height");
height.nodeValue = h;
canvas.setAttributeNode(width);
canvas.setAttributeNode(height);
ctx.drawImage(that, 0, 0, w, h);
var base64 = canvas.toDataURL('image/jpeg', scale); //压缩比例
canvas = null;
if (type == 'base64') {
let data = {
size: getBase64Size(base64),
type: type,
source: base64
}
callback(base64);
} else {
let blob = base64ToBlob(base64);
console.log('压缩后的大小', blob.size);
const blobUrl = window.URL.createObjectURL(blob); //blob地址
blob.source = blobUrl
const file = new File([blob], fileName, { type });
callback({ blob, file })
}
}
};
最新回复