h5版本是好久之前写的网上教程很多,就不放链接了,小程序版是新写的,供大家参考
话不多说直接上代码
h5方法封装版本:直接调用compressImg方法即可。
//压缩
const RESULT_ENUM = {
ORIGIN: 'ORIGIN', // 无需压缩,文件符合大小
SUCCESS: 'SUCCESS', // 压缩成功
FAIL: 'FAIL', // 压缩失败, 无法压缩到指定大小
};
//图片压缩
export async function compressImg(
file: File | undefined,
maxSize: number,
reduceCount = 0,
) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const fileBase64 = await getBase64WithFile(file);
// console.log(fileBase64,"fileBase64")
const img = await base64ToImage(fileBase64);
// console.log(img,"img")
// 用canvas绘制
let scale = 1;
if (reduceCount > 0) {
scale = 1 / Math.pow(1.2, reduceCount);
}
const originWidth = img.width;
const originHeight = img.height;
const targetWidth = originWidth * scale;
const targetHeight = originHeight * scale;
canvas.width = targetWidth;
canvas.height = targetHeight;
ctx?.clearRect(0, 0, targetWidth, targetHeight);
ctx?.drawImage(img, 0, 0, targetWidth, targetHeight);
const originBlob = await canvastoBlob(canvas, 'image/jpeg', 1);
const originSize = originBlob.size / 1024;
console.log(originBlob, originSize, maxSize, 12321);
if (originSize <= maxSize) {
return {
msg: RESULT_ENUM.ORIGIN,
base64: canvas.toDataURL('image/jpeg', 1),
};
}
const maxQualitySize = { quality: 100, size: Number.MAX_SAFE_INTEGER };
const minQualitySize = { quality: 0, size: 0 };
let quality = 100;
let count = 0; // 压缩次数
let compressFinish = false; // 压缩完成
let compressFail = false;
let compressBlob;
let needReduce = false; // 递归压缩
// 二分法最多尝试8次即可覆盖全部可能
while (!compressFinish && count < 12) {
const currentQuality = quality / 100;
compressBlob = await canvastoBlob(canvas, 'image/jpeg', currentQuality);
const compressSize = compressBlob.size / 1024;
count++;
if (compressSize === maxSize) {
console.log(`压缩完成,总共压缩了${count}次`);
compressFinish = true;
const base64 = canvas.toDataURL('image/jpeg', currentQuality);
return {
msg: RESULT_ENUM.SUCCESS,
base64,
};
}
if (compressSize > maxSize) {
maxQualitySize.quality = quality;
maxQualitySize.size = compressSize;
}
if (compressSize < maxSize) {
minQualitySize.quality = quality;
minQualitySize.size = compressSize;
}
console.log(
`第${count}次压缩,压缩后大小${compressSize},quality参数:${quality}`,
);
quality = Math.ceil((maxQualitySize.quality + minQualitySize.quality) / 2);
if (maxQualitySize.quality - minQualitySize.quality < 2) {
console.log({ minQualitySize, quality });
if (!minQualitySize.size && quality) {
quality = minQualitySize.quality;
} else if (!minQualitySize.size && !quality) {
if (quality === 0) {
needReduce = true;
}
compressFinish = true;
compressFail = true;
console.log(`压缩完成,总共压缩了${count}次`);
} else if (minQualitySize.size > maxSize) {
compressFinish = true;
compressFail = true;
needReduce = true;
console.log(`压缩完成,总共压缩了${count}次`);
} else {
console.log(`压缩完成,总共压缩了${count}次`);
compressFinish = true;
quality = minQualitySize.quality;
}
}
}
// 递归
if (needReduce) {
console.log(1, maxSize, reduceCount);
const nextReduceBlob = await canvastoBlob(canvas, 'image/jpeg', 1 / 100);
const reduceFile = generateFileFromBlob(nextReduceBlob);
return await compressImg(reduceFile, maxSize, reduceCount + 1);
} else if (compressFail) {
console.log(2);
return {
msg: RESULT_ENUM.FAIL,
base64: fileBase64,
};
}
const currentQuality = quality / 100;
compressBlob = await canvastoBlob(canvas, 'image/jpeg', currentQuality);
const compressSize = compressBlob.size / 1024;
console.log(
`最后一次压缩(即第${
count + 1
}次),quality为:${quality},大小:${compressSize}`,
);
const base64 = canvas.toDataURL('image/jpeg', currentQuality);
return {
msg: RESULT_ENUM.SUCCESS,
base64,
};
}
export function generateFileFromBlob(
compressBlob,
fileName = new Date().getTime().toString(),
) {
return new File([compressBlob], fileName, {
type: 'image/jpeg',
});
}
// canvas转成blob
export function canvastoBlob(canvas, type, quality): Promise<Blob> {
return new Promise((resolve) =>
canvas.toBlob((blob) => resolve(blob as Blob), type, quality),
);
}
// file对象转base64
export function getBase64WithFile(file): Promise<string> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result as string);
reader.onerror = (error) => reject(error);
});
}
// base64转成image
export function base64ToImage(dataURL): Promise<HTMLImageElement> {
return new Promise((resolve) => {
const img = new Image();
img.onload = () => resolve(img);
img.src = dataURL;
});
}
export const dataURLtoBlob = (dataurl) => {
const arr = dataurl.split(','),
mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]);
let n = bstr.length;
const u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], { type: mime });
};
小程序版本封装压缩图片方法:
import Taro from '@tarojs/taro';
/**
*小程序图片压缩到指定大小
* @param oldFilePath 原图地址
* @param filePath 压缩后的图片地址
* @param limitSize 图片大小kb
* @param quality 图片质量
* @param step 图片质量每次降低多少
* @param callback 回调
*/
const compressImage = (
oldFilePath: string,
filePath: string,
limitSize: number = 1024,
quality: number = 100,
step: number = 5,
callback: (base64: string) => void,
) => {
const path = filePath === '' ? oldFilePath : filePath;
Taro.getFileSystemManager().getFileInfo({
filePath: path,
success: async (res) => {
console.log(`图片压缩size:${res.size / 1024}kb`, `quality:${quality}`);
if (res.size > 1024 * limitSize) {
Taro.compressImage({
src: oldFilePath,
quality: quality - step,
success(result) {
compressImage(
oldFilePath,
result.tempFilePath,
limitSize,
quality - step,
step,
callback,
);
},
});
} else {
console.log(
`压缩成功!size:${res.size / 1024}kb`,
`quality:${quality}`,
`path:${filePath}`,
);
// const base64 = Taro.getFileSystemManager().readFileSync(filePath);
callback(filePath);
}
},
fail(res) {
callback(res.errMsg);
},
});
};
export { compressImage };
vue文件:
<template>
<div>
<img
@click="chooseImage"
:src="imgMain"
alt=""
/>
</div>
</template>
<script setup lang="ts">
import Taro from '@tarojs/taro';
import { compressImage } from '@/utils/upload/weapp';
import { compressImg, dataURLtoBlob } from '@/utils/upload/h5';
const env = process.env.TARO_ENV;
const chooseImage = async () => {
Taro.chooseImage({
count: 1, // 默认9
sizeType: ['original'], // 注意这里,设置成compressed可直接压缩,图片大小不可控,使用自定义方法压缩时配置成original获取原图
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有,在H5浏览器端支持使用 `user` 和 `environment`分别指定为前后摄像头
success: async (res) => {
console.log(res);
if (env === 'weapp') {
compressImage(res.tempFilePaths[0], (filePath) => {
Taro.uploadFile({
url: process.env.TARO_APP_BASE_URL + '666',
filePath,
name: 'frontSide', //对应FormData中的frontSide
header: {
'Content-Type': 'multipart/form-data',
withCredentials: true,
Token: '123131',
},
success(result) {
console.log(result);
},
fail(res) {
console.log(res);
},
});
});
} else {
//返回选定照片的本地文件路径列表,tempFilePath可以作为img标签的src属性显示图片
var tempFile = res.tempFiles[0].originalFileObj;
console.log(tempFile);
let imgData = await compressImg(tempFile, 300);
const blob = dataURLtoBlob(imgData.base64);
const formData = new FormData();
formData.append('frontSide', blob);
//调用后端接口上传图片
const { data } = await 哈哈哈(formData, {
headers: {
'Content-Type': 'multipart/form-data',
withCredentials: true,
},
});
console.log(data);
}
},
});
};
</script>
图片上传时,可让后端接口直接支持上传base64格式字符串