一、簡介
最近項目需要對上傳的圖片文件進行壓縮後才上傳到服務器中,於是研究了一番,下面給出詳細的壓縮方法,筆者使用的是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幾兆。