React圖片壓縮上傳統(tǒng)一處理方式
React圖片壓縮上傳統(tǒng)一處理
最近項(xiàng)目需要對(duì)上傳的圖片文件進(jìn)行壓縮后才上傳到服務(wù)器中,于是研究了一番,下面給出詳細(xì)的壓縮方法,筆者使用的是React Ant Design前端框架的Upload組件上傳圖片:
通過(guò)查看Ant Design官網(wǎng)文檔,在上傳文件前可以修改文件:
transformFile | 在上傳之前轉(zhuǎn)換文件。支持返回一個(gè) Promise 對(duì)象 | Function(file): string | Blob | File | Promise<string | Blob | File> | 無(wú) |
壓縮相關(guān)代碼
圖片壓縮的原理:實(shí)際上根據(jù)圖片大小有沒(méi)有超過(guò)預(yù)定的最大最小時(shí),如果超過(guò)指定的高度/寬度,在不怎么失真的前提下裁剪圖片,然后使用canvas畫(huà)布的drawImage()方法繪制圖片。
下面是關(guān)鍵的代碼:
//在上傳之前轉(zhuǎn)換文件 transformFile = (file) => { /** * 針對(duì)圖片進(jìn)行壓縮,如果圖片大小超過(guò)壓縮閾值,則執(zhí)行壓縮,否則不壓縮 */ //判斷是否是圖片類(lèi)型 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"); //當(dāng)開(kāi)啟圖片壓縮且圖片大小大于等于壓縮閾值,進(jìn)行壓縮 if ((fileSize >= compressThreshold) && isPictureCompress) { //判斷瀏覽器內(nèi)核是否支持base64圖片壓縮 if (typeof (FileReader) === 'undefined') { return file; } else { try { this.setState({ spinLoading: true }); return new Promise(resolve => { //聲明FileReader文件讀取對(duì)象 const reader = new FileReader(); reader.readAsDataURL(file); reader.onload = () => { // 生成canvas畫(huà)布 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; //默認(rèn)最大尺度的尺寸限制在(1920 * 1080) let maxWidth = 1920, maxHeight = 1080, ratio = maxWidth / maxHeight; //目標(biāo)尺寸 let targetWidth = originImageWidth, targetHeight = originImageHeight; //當(dāng)圖片的寬度或者高度大于指定的最大寬度或者最大高度時(shí),進(jìn)行縮放圖片 if (originImageWidth > maxWidth || originImageHeight > maxHeight) { //超過(guò)最大寬高比例 if ((originImageWidth / originImageHeight) > ratio) { //寬度取最大寬度值maxWidth,縮放高度 targetWidth = maxWidth; targetHeight = Math.round(maxWidth * (originImageHeight / originImageWidth)); } else { //高度取最大高度值maxHeight,縮放寬度 targetHeight = maxHeight; targetWidth = Math.round(maxHeight * (originImageWidth / originImageHeight)); } } // canvas對(duì)圖片進(jìn)行縮放 canvas.width = targetWidth; canvas.height = targetHeight; // 清除畫(huà)布 ctx.clearRect(0, 0, targetWidth, targetHeight); // 繪制圖片 ctx.drawImage(img, 0, 0, targetWidth, targetHeight); // quality值越小,圖像越模糊,默認(rèn)圖片質(zhì)量為0.92 const imageDataURL = canvas.toDataURL(file.type || 'image/jpeg', pictureQuality); // 去掉URL的頭,并轉(zhuǎn)換為byte const imageBytes = window.atob(imageDataURL.split(',')[1]); // 處理異常,將ascii碼小于0的轉(zhuǎn)換為大于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 }); //壓縮出錯(cuò),直接返回原file對(duì)象 return file; } } } else { //不需要壓縮,直接返回原file對(duì)象 return file; } } else { //非圖片文件,不進(jìn)行壓縮,直接返回原file對(duì)象 return file; } };
相關(guān)屬性說(shuō)明:
compressThreshold
: 5, //壓縮的閾值,圖片大小超過(guò)5M,則需要進(jìn)行壓縮isPictureCompress
: false, //是否開(kāi)啟圖片壓縮pictureQuality
: 0.92, //指定壓縮的圖片質(zhì)量,取值范圍為0~1,quality值越小,圖像越模糊,默認(rèn)圖片質(zhì)量為0.92
使用方法
<NHUpload uploadType={'file'} multiple={true} fileCountLimit={fjsl} maxFileSize={20} fileTypeLimit={fileTypeList} onChange={this.fjOnChange} isPictureCompress={true} //是否開(kāi)啟圖片壓縮 pictureQuality={0.5} //圖片質(zhì)量 compressThreshold={1} //壓縮閾值 />
在使用時(shí),我們可以根據(jù)業(yè)務(wù)需求動(dòng)態(tài)設(shè)置需要壓縮的閾值,圖片質(zhì)量等等,對(duì)圖片壓縮可以大大節(jié)省服務(wù)器的資源,現(xiàn)在手機(jī)隨便拍一張照片就是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; // 修改目標(biāo)圖片名字 // 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的最后面中括號(hào)和引號(hào)是不轉(zhuǎn)譯的 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 需要返回的類(lèi)型blob,file * @param quality 圖片壓縮比 0-1,數(shù)字越小,圖片壓縮越小 * @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) } }) }) }
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
React-Hooks之useImperativeHandler使用介紹
這篇文章主要為大家介紹了React-Hooks之useImperativeHandler使用介紹,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07React通過(guò)父組件傳遞類(lèi)名給子組件的實(shí)現(xiàn)方法
React 是一個(gè)用于構(gòu)建用戶界面的 JAVASCRIPT 庫(kù)。這篇文章主要介紹了React通過(guò)父組件傳遞類(lèi)名給子組件的方法,需要的朋友可以參考下2017-11-11React?中如何將CSS?visibility?屬性設(shè)置為?hidden
這篇文章主要介紹了React中如何將CSS?visibility屬性設(shè)置為?hidden,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-05-05JS中使用react-tooltip插件實(shí)現(xiàn)鼠標(biāo)懸浮顯示框
前段時(shí)間遇到的一個(gè)需求,要求鼠標(biāo)懸停顯示使用描述, 用到了react-tooltip插件,今天寫(xiě)一個(gè)總結(jié),感興趣的朋友跟隨小編一起看看吧2019-05-05react-json-editor-ajrm解析錯(cuò)誤與解決方案
由于歷史原因,項(xiàng)目中 JSON 編輯器使用的是 react-json-editor-ajrm,近期遇到一個(gè)嚴(yán)重的展示錯(cuò)誤,傳入編輯器的數(shù)據(jù)與展示的不一致,這是產(chǎn)品和用戶不可接受的,本文給大家介紹了react-json-editor-ajrm解析錯(cuò)誤與解決方案,需要的朋友可以參考下2024-06-06react中關(guān)于Context/Provider/Consumer傳參的使用
這篇文章主要介紹了react中關(guān)于Context/Provider/Consumer傳參的使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-09-09React找不到模塊“./index.module.scss”或其相應(yīng)的類(lèi)型聲明及解決方法
這篇文章主要介紹了React找不到模塊“./index.module.scss”或其相應(yīng)的類(lèi)型聲明及解決方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-09-09react-native?父函數(shù)組件調(diào)用類(lèi)子組件的方法(實(shí)例詳解)
這篇文章主要介紹了react-native?父函數(shù)組件調(diào)用類(lèi)子組件的方法,通過(guò)詳細(xì)步驟介紹了React 函數(shù)式組件之父組件調(diào)用子組件的方法,代碼簡(jiǎn)單易懂,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-09-09