Why the front end to compress the image

Recently doing a mobile terminal function of h5 to upload pictures, originally this feature is not complicated, just need to upload image files by axios service side, but considering the now mobile phones with camera function is very powerful, can easily WuLiuZhao literally a photograph, and upload pictures are server-side requirements must be less than two million, and directly pass this big picture, Bandwidth is too much, so compression of the front end is a necessary link.

Effect of compression



First introduce the general process of compression

  1. Get the image file to upload using the native input tag
  2. Convert the image file to an IMG element tag
  3. Zip and draw the HTMLImageElement on the canvas
  4. Convert canvas drawn images into blob files
  5. Finally, the BLOb file is passed to the server
  6. Done!

Now let’s look at the detailed steps

Considering the completeness of the article and steps, I will write down every detail, even if some things are very basic.

1. Use the Input tag to get the image file resource

The native input tag allows the user to select files by setting the type attribute to File, setting accept to limit the type of files selected, and binding the onchange event to retrieve the selected files

<input type="file" accept="image/*" />Copy the code

Click the control to trigger the focus, open the file explorer, select the file and confirm, will trigger the change event, so you can get the selected file in the change event callback, it looks like this



2. Read files into img tag elements

Once you have the image file, turn it into an HTMLImageElement, which is a normal IMG tag, using the FileReader constructor.

Create a new instance of img and fileReader, read the image file via fileReader’s readAsDataURL API, return an encoded Base64 string, and assign this string to the img SRC attribute. This completes the conversion of the image file to the HTMLImageElement.

Const img = new Image() const reader = new fileReader ()// readAsDataURL(file) reader.onload = function(e){ img.src = e.target.result }Copy the code

Transformation of HTMLImageElement



3. Canvas compression, core steps

Once you have the transformed IMG element, first extract the width of the element, which is the width of the actual image file.

const { width: originWidth, height: originHeight } = imgCopy the code

Then define a maximum width height, if this limit width height is exceeded, scale equally

 // Maximum size limit
 const maxWidth = 1000,maxHeihgt = 1000
 // The target size that needs to be compressed
 let targetWidth = originWidth, targetHeight = originHeight
 // Calculate the size of a scaled image when the maximum limit is exceeded
 if (originWidth > maxWidth || originHeight > maxHeight) {
      if (originWidth / originHeight > 1) {
        / / wide images
        targetWidth = maxWidth
        targetHeight = Math.round(maxWidth * (originHeight / originWidth))
      } else {
        / / high pictures
        targetHeight = maxHeight
        targetWidth = Math.round(maxHeight * (originWidth / originHeight))
      }
   }Copy the code

After calculating the size to be compressed, create canvas instance, set the width height of canvas to the size after compression calculation, and draw img on it

// Create canvas
const canvas = document.createElement('canvas')
const context = canvas.getContext('2d')

// Set the width to the same size as the image to be compressed
 canvas.width = targetWidth
 canvas.height = targetHeight
 context.clearRect(0.0, targetWidth, targetHeight)
 // Draw img onto the canvas
 context.drawImage(img, 0.0, targetWidth, targetHeight)Copy the code

4. Convert to BLOB files

Once the canvas is drawn, you can use toBlob to convert the image into a BLOB file. This API takes three incoming arguments

canvas.toBlob(callback, type, encoderOptions);Copy the code

In the callback function, you can get the transformed blob file with the type of image to be converted, PNG by default.

EncoderOptions specifies the image display quality if the image format is image/ JPEG or image/webp.

So if we just want to compress JPG or WebP images, we don’t need to do part 3, just use the API and fill in the desired quality parameters. In practice, however, there are still multiple image formats to consider, so it is necessary to use the process in Part 3.

The transformed bloB looks like this



5. Upload the BLOb and you’re done.

Complete code implementation

Since there are asynchronous callback operations throughout the process, I use async to implement synchronous execution of asynchronous code

// Convert file to img before compression
function readImg(file)  {
  return new Promise((resolve, reject) = > {
    const img = new Image()
    const reader = new FileReader()
    reader.onload = function(e) {
      img.src = e.target.result
    }
    reader.onerror = function(e) {
      reject(e)
    }
    reader.readAsDataURL(file)
    img.onload = function() {
      resolve(img)
    }
    img.onerror = function(e) {
      reject(e)
    }
  })
}Copy the code

/** * compressed image *@param img compressed img object *@paramtypeFile type converted after compression * @param MX trigger compression maximum width limit * @param MH trigger compression maximum height limit */function compressImg(img, type, mx, mh) {
  return new Promise((resolve, reject) => {
    const canvas = document.createElement('canvas')
    const context = canvas.getContext('2d'Const {width: originWidth, height: originHeight} = img // maximum size limit const maxWidth = mx const maxHeight = mh // target sizelet targetWidth = originWidth
    let targetHeight = originHeight
    if (originWidth > maxWidth || originHeight > maxHeight) {
      if(originWidth/originHeight > 1) {// Width picture targetWidth = maxWidth targetHeight = math.round (maxWidth * (originHeight /) originWidth)) }else{// high image targetHeight = maxHeight targetWidth = math.round (maxHeight * (originWidth/originHeight))}} canvas.width = targetWidth canvas.height = targetHeight context.clearRect(0, 0, targetWidth, DrawImage (img, 0, 0, targetWidth, targetHeight) canvas.toblob (function(blob) {
      resolve(blob)
    }, type || 'image/png')})}Copy the code

General implementation process, specific can be changed according to requirements

async function upload(file){
   const img = await readImg(file)
   const blob = await compressImg(img, file.type, 1000, 1000)
   const formData = new FormData()
   formData.append('file', blob, 'xxx.jpg')
    axios.post('http://xxx.com/api',formData)
}
upload(file).catch(e => console.log(e))Copy the code


The last

Thank you for reading, hope to progress with you together, feel helpful, click a like oh 😁😁