uniapp uploads pictures and uses compressorjs to process picture compression and automatically calculate the compression ratio

background:

(1) There is a problem that the uniapp image upload is too large and takes a long time to upload. The image needs to be compressed and uploaded.

(2) The image compression ratio varies with the size.

This article uses uniapp’s uni-file-picker to upload pictures, and uview’s picture upload can also be used for reference.

1. Image compression first install compressorjs

npm install compressorjs --save

2. Create imageCompress.js and use image compression. The type returned after compression in this article is blob

import Compressor from 'compressorjs';
// Only images in jpeg format can be converted
/**
 * @param image image
 * @param backType The type blob, file that needs to be returned
 * @param quality Image compression ratio 0-1, the smaller the number, the smaller the image compression
 * @returns
 */
export default function ImageCompressor(image, backType, quality) {
    return new Promise((resolve, reject) => {
        new Compressor(image, {
            quality: quality || 0.6,
            success(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)
                }
            },
            error(err) {
                console.log('Image compression failed ---->>>>>', err)
                reject(err)
            }
        })
    })
}

3. Create ConvertImage.js to convert non-jpeg images into jpeg images

// The idea is to create a picture, set the file equal to the picture, then create a canvas layer, scale the canvas proportionally,
//Then use the drawImage of the canvas to combine the picture with the canvas, and then convert the base64 of the canvas into a file
export default function ConvertImage(file) {
    return new Promise((resolve, reject) => {
        const fileName = file.name.substring(0, file.name.indexOf('.'));
        let reader = new FileReader(); //read file
        reader.readAsDataURL(file);
        reader. onloadend = function (e) {
            let image = new Image() //Create a new img tag (not yet embedded in the DOM node)
            image.src = e.target.result //Set the path of the image to the file path
            image.onload = function () {
                let canvas = document. createElement('canvas'),
                    context = canvas. getContext('2d'),
                    imageWidth = image. width,
                    imageHeight = image. height,
                    data = ''
                canvas.width = imageWidth
                canvas.height = imageHeight
 
                context. drawImage(image, 0, 0, imageWidth, imageHeight)
                data = canvas.toDataURL('image/jpeg')
                var newfile = dataURLtoFile(data, fileName + '.jpeg');
                resolve(newfile)
            }
        }
    })
}
function dataURLtoFile(dataurl, filename) { // base64 to file object
    let arr = dataurl. split(','),
        mime = arr[0].match(/:(.*?);/)[1],
        bstr = atob(arr[1]), n = bstr. length, u8arr = new Uint8Array(n);
    while (n--) {
        u8arr[n] = bstr. charCodeAt(n);
    }
    return new File([u8arr], filename, { type: mime }); // converted to jpeg format
}

4. Compression ratio calculation

 getCompressionRatio(fileSize) {
const multiple = (fileSize / this.fileMaxSize).toFixed(2) // Get the file size multiple and generate quality ratio
alert(fileSize + "===" + this.fileMaxSize + "===" + multiple)
let compressionRatio = 1
if(multiple > 5) {
compressionRatio = 0.5
} else if (multiple > 4) {
compressionRatio = 0.6
} else if (multiple > 3) {
compressionRatio = 0.7
} else if (multiple > 2) {
compressionRatio = 0.8
} else if (multiple > 1) {
compressionRatio = 0.9
} else {
compressionRatio = 2
}
return compressionRatio;
},

5. blob to bloburl

Use uni.uploadFile to upload files, filePath uses the local address of bloburl, blob:http://localhost:9092/e9a9042b-feab-4fed-99ff-81c1e6efdece

 uni.uploadFile({
                     url: '/prod-api/common/upload', //backend interface for processing images and returning image addresses
                     filePath:tempFilePath,
                      ...

})

Therefore, it is necessary to convert the compressed blob file into bloburl so that it can be displayed and uploaded

let url = window. URL. createObjectURL(newImg)

6. The whole code is as follows

<template>
<view class="u-page">
<view class="u-demo-block">
<view class="u-demo-block__content">
<!-- Note, if you need to be compatible with WeChat applets, it is best to set the rules rules through the setRules method -->
<u--form
labelPosition="left"
:model="form"
ref="form1"
>
<u-form-item
label="Select Photo"
prop="form.problemDescription"
borderBottom
labelWidth="80"
ref="item3"
>
<uni-file-picker
:source-type="sourceType"
:sizeType="sizeType"
v-model="fileList1"
mode="grid"
@select="select"
@progress="progress"
@success="success"
@delete ="deletephoto"
@fail="fail"
ref="upload"
limit="9"
/>
</uni-file-picker>
</u-form-item>
</u--form>
</view>
</view>
</view>
</template>

<script>
import { getToken } from "@/utils/auth";
import ImageCompressor from "@/utils/imageCompressor"
import ConvertImage from "@/utils/ConvertImage"
export default {
data() {
return {
sourceType: ['album', 'camera'],
timer: '',
sizeType: ['compressed'],
fileMaxSize: 2 * 1024 * 1024, // The default maximum is 2M
fileMinSize: 50 * 1024, // The minimum is 50KB
form: {},
fileList1: {},
imgSrc:{},
images: [],
}
},
onReady() {

},
mounted() {
// this. nowTimes()
},
onLoad(option) {
\t\t
},
methods: {
\t\t
// // Select upload trigger function
async select(e) {
// According to the number of selected pictures, call the upload function multiple times
console.log(e.tempFilePaths)
let promises = []
for (let i = 0; i < e. tempFilePaths. length; i ++ ) {
let img=e.tempFiles[i].file
const fileSize = img. size
const fileName = img.name''
if (fileSize > this. fileMaxSize) {
const compressionRatio = this. getCompressionRatio(fileSize)
if (compressionRatio > 1) {
uni.$u.toast('file' + fileName + 'greater than 10M')
return false
}
const fileType = img.name.substring(img.name.indexOf('.') + 1);
// Determine whether the file is jpeg or not, convert it to jpeg
if (!['jpeg', 'jpg'].includes(fileType)) {
console. log(fileType)
img = await ConvertImage(img); // convert the file in jpeg format
}
console. log(img)
let newImg = await ImageCompressor(img, 'blob', compressionRatio); //image compression
\t\t\t
let url = window. URL. createObjectURL(newImg)
const promise = this. uploadFiles(url)
promises. push(promise)
}
}
Promise.all(promises).then(()=>{
})
},
\t\t\t
getCompressionRatio(fileSize) {
const multiple = (fileSize / this.fileMaxSize).toFixed(2) // Get the file size multiple and generate quality ratio
alert(fileSize + "===" + this.fileMaxSize + "===" + multiple)
let compressionRatio = 1
if(multiple > 5) {
compressionRatio = 0.5
} else if (multiple > 4) {
compressionRatio = 0.6
} else if (multiple > 3) {
compressionRatio = 0.7
} else if (multiple > 2) {
compressionRatio = 0.8
} else if (multiple > 1) {
compressionRatio = 0.9
} else {
compressionRatio = 2
}
return compressionRatio;
},
// upload function
async uploadFiles(tempFilePath){
let that=this
await uni.uploadFile({
url: '/prod-api/common/upload', //backend interface for processing images and returning image addresses
filePath:tempFilePath,
name: 'file',
header: {
Authorization: "Bearer " + getToken(),
},
success: res => {
let data=JSON.parse(res.data) //The returned string needs to be converted into an object format
let imageName=data.fileName
that.images.push(imageName)
uni.showToast({ title: 'Uploaded successfully', icon: "success" });
},
fail: () => {
console.log("err");
uni.showToast({ title: 'Upload failed', icon: "error" });

}
})
},
// remove image function
async deletephoto(){
this.fileList1 = {}
}
           
}
}
</script>