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幾兆。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章