React图片压缩上传统一处理方式
作者:每天都要进步一点点
这篇文章主要介绍了React图片压缩上传统一处理方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
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'; /** * @param image 图片 * @param backType 需要返回的类型blob,file * @param quality 图片压缩比 0-1,数字越小,图片压缩越小 * @returns */ 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) } }) }) }
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。