Convert Any Images to JPEG and WebP, Compress, and Resize on the Client Side

Image Service
Image Service

When creating a website that lets users publish content or User-Generated Content, we need to sanitize user inputs, especially images. Below are the concerns:

Let me explain why PNG, GIF, and SVG are incorrect formats like what I said above.

Some users have no idea about those formats. We should let them upload those photos and handle the rest.

Handling the rest on the server side is the worst. Let's imagine, your app has one million users, each user uploads a photo, and the server will transform the format, resize, and reduce the quality. That will be a pain for the server. Even you queue those photos or scale the server. The cost will be expensive or maybe can cause the server to die.

The best approach is to process all of them on the client side. Maybe you can't do this approach if you have an AI image compressor that needs to run on the server side.

Here are the steps of a process:

  1. Transform all images to JPEG or WebP.
  2. Resize (and crop) images.
  3. Reduce the quality by up to 80%.

The reason we need to transform all photos' formats of PNG, GIF, or SVG to JPEG is that it is the best format.

WebP is a modern image format developed by Google that provides superior lossless and lossy compression for images on the web.

WebP is cool. But I think it is optional because old browsers didn't support it. For more information, you can visit https://developers.google.com/speed/webp/faq.

I created a web app with TypeScript and React to demonstrate it and the result is amazing. You can try it at https://image.aristorinjuang.com. I uploaded the original image of the photo that I used on the homepage. The image is already in JPEG but the file size is about 8 MB. The JPEG version of the result is about 243 KB which is pretty amazing because the quality is reduced to 80%. The WebP version of the result is about 2 MB which is also amazing because it keeps the quality to 100%.

After the images are ready, we only need to send them to the server side on the POST method with multipart/form-data for input elements. For more information, you can visit https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST. Then, the server will save the meta information to the database, store images in the storage, and publish them.

Along with TailwindCSS, TypeScript, and React, I used React Hook. So, I used React Context instead of Redux. Also thanks to these libraries:

In some cases, we create copies with different image sizes which we call thumbnails. The best approach is to do this process on the client side too. Here is the main code to generate images.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
const thumbnailCategories: ThumbnailCategories = [
  {
    height: 1080,
    suffix: 'original',
    width: 1920
  }
];
const generateImages = (
  file: any,
  setJPEG: (value: string) => void,
  setWebP: (value: string) => void,
  downloadList: Props[]
) => {
  for (let thumbnailCategory of thumbnailCategories) {
    try {
      Resizer.imageFileResizer(
        file,
        thumbnailCategory.width,
        thumbnailCategory.height,
        "JPEG",
        80,
        0,
        (uri) => {
          if (thumbnailCategory.suffix === 'original') {
            setJPEG(String(uri))
            downloadList.push({
              filename: 'original.jpg',
              href: String(uri)
            })
          }
        }
      );
    } catch (err) {
      console.error(err);
    }
  
    try {
      Resizer.imageFileResizer(
        file,
        thumbnailCategory.width,
        thumbnailCategory.height,
        "WEBP",
        100,
        0,
        (uri) => {
          if (thumbnailCategory.suffix === 'original') {
            setWebP(String(uri))
            downloadList.push({
              filename: 'original.webp',
              href: String(uri)
            })
          }
        }
      );
    } catch (err) {
      console.error(err);
    }
  }
}

The thumbnail categories are still limited to only one, which is original. We can add more. There are many ways to do that, hard coded or user inputs. Maybe we also need to add an image cropping library to crop images to the proportional ratio.

I created the demonstration for the web along with its unit tests in a limited time, one day (or two days along with this article). I hope this article helps you to build a high-performance system, especially when handling user images. Mostly, I didn't talk about the source code, but you are free to see the source code at https://github.com/aristorinjuang/image-service or even contribute to it.

Related Articles