網上圖片壓縮文章抄來抄去,真沒意思,我自己寫一個
import React from 'react'; // import logo from './logo.svg'; import './App.css'; function App() { const handleChange = e => { e.persist() // 為了看到原生file的內容,不加這句,原生file內容會被react隱藏 let fileObj = e.target.files[0] // console.log(e) console.log(fileObj) // 設定標准上限為1MB,進行壓縮處理 compress(fileObj, 1024, (data) => { console.log(data) }) } // 異步讀取圖片的promise const loadImageAsync = (url) => { return new Promise(function (resolve, reject) { const image = new Image() image.onload = function () { resolve(image) }; image.onerror = function () { reject(new Error('Could not load image at ' + url)) }; image.src = url }) } // 異步轉換成base64編碼的promise const fileToImgAsync = (file) => { return new Promise(function (resolve, reject) { const reader = new FileReader() reader.onload = function (e) { resolve(e.target.result); }; reader.onerror = function () { reject(new Error('readAsDataURL:fail')) }; reader.readAsDataURL(file) }); } const downloadFileByBlob = (blobUrl, filename) => { const a = document.createElement('a') a.download = filename a.style.display = 'none' a.href = blobUrl // 觸發點擊 document.body.appendChild(a) a.click() // 然后移除 document.body.removeChild(a) } // async 搭配 promise 使用 const compress = async (file, maxSizeKB, succFunc) => { if (file.size > 3 * 1024 * 1024) { let rate = 0 // 壓縮率 // 文件轉圖片 const dataUrl = await fileToImgAsync(file) // 圖片轉畫布 const image = await loadImageAsync(dataUrl) // console.log(dataUrl, image) // 文件大小KB, file.size給的是字節Byte const fileSizeKB = file.size / 1024 console.log(fileSizeKB) // 當圖片大小超標,才進行壓縮 if (fileSizeKB > maxSizeKB) { // 計算壓縮率 rate = (fileSizeKB - maxSizeKB) / fileSizeKB console.log('壓縮率:', rate) console.log('壓縮后文件大小:', fileSizeKB * (1 - rate), 'kb') } // 畫布執行壓縮 let canvas = document.createElement('canvas') let context = canvas.getContext('2d') const cvWidth = image.width * (1 - rate) const cvHeight = image.height * (1 - rate) console.log(image.width, image.height, cvWidth, cvHeight) canvas.height = cvHeight canvas.width = cvWidth context.clearRect(0, 0, cvWidth, cvHeight) context.drawImage(image, 0, 0, cvWidth, cvWidth) // 導出圖片 canvas.toBlob((blob) => { // 方式一:下載到本地 // const blobUrl = window.URL.createObjectURL(blob) // downloadFileByBlob(blobUrl, file.name) // 方式二:生成網頁可讀取的對象 const newImage = new File([blob], file.name, { type: file.type }); succFunc(newImage) }); } } return ( <div className="App"> <input type="file" id="file" onChange={handleChange} /> </div> ); } export default App;
其實我們可以發現一個問題,導出的圖片,在window系統中,大小是 207kb並不是1024kb,很明顯size的比例用在width height的比例上有出入,要加入調節因子,因此算法還可以優化,如下
import React from 'react'; // import logo from './logo.svg'; import './App.css'; function App() { const handleChange = e => { e.persist() // 為了看到原生file的內容,不加這句,原生file內容會被react隱藏 let fileObj = e.target.files[0] // console.log(e) console.log(fileObj) // 設定標准上限為1MB,進行壓縮處理 compress(fileObj, 1024, (data) => { console.log(data) }) } // 異步讀取圖片的promise const loadImageAsync = (url) => { return new Promise(function (resolve, reject) { const image = new Image() image.onload = function () { resolve(image) }; image.onerror = function () { reject(new Error('Could not load image at ' + url)) }; image.src = url }) } // 異步轉換成base64編碼的promise const fileToImgAsync = (file) => { return new Promise(function (resolve, reject) { const reader = new FileReader() reader.onload = function (e) { resolve(e.target.result); }; reader.onerror = function () { reject(new Error('readAsDataURL:fail')) }; reader.readAsDataURL(file) }); } const downloadFileByBlob = (blobUrl, filename) => { const a = document.createElement('a') a.download = filename a.style.display = 'none' a.href = blobUrl // 觸發點擊 document.body.appendChild(a) a.click() // 然后移除 document.body.removeChild(a) } // async 搭配 promise 使用 const compress = async (file, maxSizeKB, succFunc) => { if (file.size > maxSizeKB * 1024) { let rate = 0 // 壓縮率 // 文件轉圖片 const dataUrl = await fileToImgAsync(file) // 圖片轉畫布 const image = await loadImageAsync(dataUrl) // console.log(dataUrl, image) // 文件大小KB, file.size給的是字節Byte const fileSizeKB = file.size / 1024 console.log(fileSizeKB) // 當圖片大小超標,才進行壓縮 if (fileSizeKB > maxSizeKB) { // 計算壓縮率 rate = (fileSizeKB - maxSizeKB) / fileSizeKB console.log('壓縮率:', rate) console.log('壓縮后文件大小:', fileSizeKB * (1 - rate), 'kb') } // 糾正因子,不加會導致壓縮出的文件太小 const factor = 0.2 // 畫布執行壓縮 let canvas = document.createElement('canvas') let context = canvas.getContext('2d') const cvWidth = image.width * (1 - rate + factor) const cvHeight = image.height * (1 - rate + factor) console.log(image.width, image.height, cvWidth, cvHeight) canvas.height = cvHeight canvas.width = cvWidth context.clearRect(0, 0, cvWidth, cvHeight) context.drawImage(image, 0, 0, cvWidth, cvWidth) // 導出圖片 canvas.toBlob((blob) => { // 方式一:下載到本地 const blobUrl = window.URL.createObjectURL(blob) downloadFileByBlob(blobUrl, file.name) // 方式二:生成網頁可讀取的對象 // const newImage = new File([blob], file.name, { type: file.type }); // succFunc(newImage) }); } } return ( <div className="App"> <input type="file" id="file" onChange={handleChange} /> </div> ); } export default App;
就這樣吧,勉強能用,現在導出的都有800-900kb,還行吧