React图片压缩上传统一处理
最近项目需要对上传的图片文件进行压缩后才上传到服务器中,于是研究了一番,下面给出详细的压缩方法,笔者使用的是React Ant Design前端框架的Upload组件上传图片:
通过查看Ant Design官网文档,在上传文件前可以修改文件:
transformFile | 在上传之前转换文件。支持返回一个 Promise 对象 | Function(file): string | Blob | File | Promise<string | Blob | File> | 无 |
压缩相关代码
图片压缩的原理:实际上根据图片大小有没有超过预定的最大最小时,如果超过指定的高度/宽度,在不怎么失真的前提下裁剪图片,然后使用canvas画布的drawImage()方法绘制图片。
下面是关键的代码:
//在上传之前转换文件
transformFile = (file) => {
//判断是否是图片类型
if (this.checkIsImage(file.name)) {
const {compressThreshold = 5, isPictureCompress = false, pictureQuality = 0.92} = this.props;
let fileSize = file.size / 1024 / 1024;
// console.log('before compress, the file size is : ', fileSize + "M");
//当开启图片压缩且图片大小大于等于压缩阈值,进行压缩
if ((fileSize >= compressThreshold) && isPictureCompress) {
//判断浏览器内核是否支持base64图片压缩
if (typeof (FileReader) === 'undefined') {
return file;
} else {
try {
this.setState({
spinLoading: true
});
return new Promise(resolve => {
//声明FileReader文件读取对象
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => {
// 生成canvas画布
const canvas = document.createElement('canvas');
// 生成img
const img = document.createElement('img');
img.src = reader.result;
img.onload = () => {
const ctx = canvas.getContext('2d');
//原始图片宽度、高度
let originImageWidth = img.width, originImageHeight = img.height;
//默认最大尺度的尺寸限制在(1920 * 1080)
let maxWidth = 1920, maxHeight = 1080, ratio = maxWidth / maxHeight;
//目标尺寸
let targetWidth = originImageWidth, targetHeight = originImageHeight;
//当图片的宽度或者高度大于指定的最大宽度或者最大高度时,进行缩放图片
if (originImageWidth > maxWidth || originImageHeight > maxHeight) {
//超过最大宽高比例
if ((originImageWidth / originImageHeight) > ratio) {
//宽度取最大宽度值maxWidth,缩放高度
targetWidth = maxWidth;
targetHeight = Math.round(maxWidth * (originImageHeight / originImageWidth));
} else {
//高度取最大高度值maxHeight,缩放宽度
targetHeight = maxHeight;
targetWidth = Math.round(maxHeight * (originImageWidth / originImageHeight));
}
}
// canvas对图片进行缩放
canvas.width = targetWidth;
canvas.height = targetHeight;
// 清除画布
ctx.clearRect(0, 0, targetWidth, targetHeight);
// 绘制图片
ctx.drawImage(img, 0, 0, targetWidth, targetHeight);
// quality值越小,图像越模糊,默认图片质量为0.92
const imageDataURL = canvas.toDataURL(file.type || 'image/jpeg', pictureQuality);
// 去掉URL的头,并转换为byte
const imageBytes = window.atob(imageDataURL.split(',')[1]);
// 处理异常,将ascii码小于0的转换为大于0
const arrayBuffer = new ArrayBuffer(imageBytes.length);
const uint8Array = new Uint8Array(arrayBuffer);
for (let i = 0; i < imageBytes.length; i++) {
uint8Array[i] = imageBytes.charCodeAt(i);
}
let mimeType = imageDataURL.split(',')[0].match(/:(.*?);/)[1];
let newFile = new File([uint8Array], file.name, {type: mimeType || 'image/jpeg'});
// console.log('after compress, the file size is : ', (newFile.size / 1024 / 1024) + "M");
resolve(newFile);
};
};
reader.onerror = () => {
this.setState({
spinLoading: false
});
return file;
}
}).then(res => {
this.setState({
spinLoading: false
});
return res;
}).catch(() => {
this.setState({
spinLoading: false
});
return file;
});
} catch (e) {
this.setState({
spinLoading: false
});
//压缩出错,直接返回原file对象
return file;
}
}
} else {
//不需要压缩,直接返回原file对象
return file;
}
} else {
//非图片文件,不进行压缩,直接返回原file对象
return file;
}
};
相关属性说明:
compressThreshold
: 5, //压缩的阈值,图片大小超过5M,则需要进行压缩isPictureCompress
: false, //是否开启图片压缩pictureQuality
: 0.92, //指定压缩的图片质量,取值范围为0~1,quality值越小,图像越模糊,默认图片质量为0.92
使用方法
<NHUpload
uploadType={'file'}
multiple={true}
fileCountLimit={fjsl}
maxFileSize={20}
fileTypeLimit={fileTypeList}
onChange={this.fjOnChange}
isPictureCompress={true} //是否开启图片压缩
pictureQuality={0.5} //图片质量
compressThreshold={1} //压缩阈值
/>
在使用时,我们可以根据业务需求动态设置需要压缩的阈值,图片质量等等,对图片压缩可以大大节省服务器的资源,现在手机随便拍一张照片就是10几兆。
React图片压缩工具(可下载)
用到的插件:compressorjs
示例
ExampleCanvas.js
import React from 'react';
import { compressorImage } from './Compressor'
export default class UploadPic extends React.Component {
constructor(props) {
super(props);
this.state = {
previewPic: '',
laterPic: ''
};
this.handleUpload = this.handleUpload.bind(this);
this.downloadImg = this.downloadImg.bind(this);
}
downloadImg(){
// console.log('download',this.state.laterPic);
var blob=this.dataURLtoBlob(this.state.laterPic)
const aLink = document.createElement('a');
document.body.appendChild(aLink);
aLink.style.display='none';
const objectUrl = window.URL.createObjectURL(blob);
aLink.href = objectUrl;
// 修改目标图片名字
// aLink.download = 'a.png';
aLink.download =document.getElementById('file').value.substring(document.getElementById('file').value.lastIndexOf('\\') + 1);
aLink.click();
}
dataURLtoBlob(dataurl) {
var arr = dataurl.split(',');
//注意base64的最后面中括号和引号是不转译的
var _arr = arr[1].substring(0,arr[1].length-2);
var mime = arr[0].match(/:(.*?);/)[1],
bstr =atob(_arr),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], {
type: mime
});
}
handleUpload(e) {
// console.log('啊哈!', e.target.files[0]);
var myFile = this.A(e.target.files[0]);
// console.log('---------myFile----------', myFile);
const reader = new FileReader();
reader.readAsDataURL(e.target.files[0]);
reader.onload = function (e) {
// console.log(e.target.result); // 上传的图片的编码
this.setState({
previewPic:e.target.result
});
}.bind(this);
}
A = async (file) => {
var myfile = await compressorImage(file, 'file', 0.6)
// console.log('----myfie-----',myfile);
const reader = new FileReader();
reader.readAsDataURL(myfile);
reader.onload = function (e) {
// console.log(e.target.result); // 上传的图片的编码
this.setState({
previewPic:this.state.previewPic,
laterPic: e.target.result
});
}.bind(this);
return myfile
}
render() {
const { previewPic, laterPic } = this.state;
return (
<div id="upload-pic">
<input type="file" id='file' className="file" onChange={this.handleUpload} />
<div><img src={previewPic} alt="" style={{ width: '675px' }} /></div>
<div><img src={laterPic} alt="" style={{ width: '675px' }} /></div>
<button onClick={this.downloadImg} >download</button>
</div>
)
}
}
核心工具
Compressor.js
import React from 'react'
import Compressor from 'compressorjs';
export const compressorImage = (image, backType, quality) => {
// console.log('image, backType, quality',image, backType, quality);
return new Promise((resolve, reject) => {
new Compressor(image, {
quality: quality || 0.8,
mimeType :'image/jpeg',
success(result) {
// console.log('result', result)
let file = new File([result], image.name, { type: image.type })
if (!backType || backType == 'blob') {
resolve(result)
} else if (backType == 'file') {
resolve(file)
} else {
resolve(file)
}
console.log('图片压缩成功---->>>>>')
},
error(err) {
console.log('图片压缩失败---->>>>>', err)
reject(err)
}
})
})
}
以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。