0

How can I convert images to WEBP format before sending them to AWS S3 in my NUXT app?

I have a photo upload on my website, I would like to convert the images from the file input to WEBP format before uploading to the Amazon web service. Unlike NodeJS where I can import sharp and use it to convert the images to WEBP format, it is not the case here as I get an error like the like below

Failed to compile with 4 errors                                                                                               friendly-errors 01:16:19  

These dependencies were not found:                                                                                                    friendly-errors 01:16:19  
                                                                                                                                      friendly-errors 01:16:19  
* child_process in ./node_modules/detect-libc/lib/detect-libc.js, ./node_modules/sharp/lib/libvips.js                                 friendly-errors 01:16:19
* fs in ./node_modules/detect-libc/lib/detect-libc.js, ./node_modules/sharp/lib/libvips.js                                            friendly-errors 01:16:19  
                                                                                                                                      friendly-errors 01:16:19  
To install them, you can run: npm install --save child_process fs  

and I would like to convert the images like in my code below

        drop(e) {
            e.preventDefault();
            e.stopPropagation();
            e.target.classList.remove('solid');
            const files = e.dataTransfer.files;
            this.handleFiles(files);
        },
        onFilePicked(e) {
            e.preventDefault();
            e.stopPropagation();
            const files = e.target.files;
            this.handleFiles(files);
        },
        saveToBackend(file, result) {
            // compress image
            // save to aws
        },
        readFiles(file) {
            const reader = new FileReader();
            reader.readAsDataURL(file)
            reader.onload = () => {
                const uploading = this.uploading
                const result = reader.result
                uploading.push(result)
                this.uploading = uploading
                

                // upload to aws
                this.saveToBackend(file, result)
            }
        },
        handleFiles(files) {
            const Files = Array.from(files);
            Files.forEach(file => {
                // check if image is a valid image
                if (file.type.includes('image')) {
                    // display the image
                    return this.readFiles(file)
                }
                return
            });
            console.log(Files, "loaded files");
        },

and for the sharp plugin

import vue from "vue"
import sharp from "sharp"
vue.use(sharp)

please how can I compress the images?

Opeyemi Odedeyi
  • 766
  • 1
  • 10
  • 38
  • @kissu your answer was only able to explain why what I was doing could not work but it didn't offer any solution. But I was eventually able to find a solution, that converts any image to a WEBP format without using any package before I can send the image to AWS S3. I had to convert the image to a canvas and then convert the image to WEBP format. I will post my solution below. – Opeyemi Odedeyi Jul 16 '21 at 18:30
  • I cannot explain how to setup a server in between 2 apps in a StackOverflow answer so far. It's great that you found a way of converting an image to `.webp`, I hope that there will not be too much quality lose in the process. Eager to see your answer. – kissu Jul 16 '21 at 18:52

3 Answers3

2

you could use the packages imagemin and imagemin-webp as answered here: Convert Images to webp with Node

Thomas K.
  • 121
  • 1
  • 10
2

As I've explained you in your previous question, you cannot use a Node.js plugin into a client side app, especially when this one is already running and especially if you're hosting it as target: static on some Vercel or alike platform.

On top of this, image processing is pretty heavy in terms of required processing. So, having an external server that is doing this as a middleware is the best idea. You'll be able to make a load balancer, allocate auto-scaling, prevent a client side timeout and allow for a simpler way to debug things (maybe even more benefits actually).
You could maybe even do it on a serverless function, if you will not be bothered to much with slower cold starts.

TLDR:

  • simple and efficient solution, put a Node.js server in-between your Nuxt and your S3 bucket
  • more affordable one but more complex, call a serverless function for this (not even sure that this will be performant)
  • wait for Nuxt3 with Nitro, and make some shenigans with a local serviceWorker and Cloudflare workers, in edge-rendering (not even sure that this is the most adapted way of handling your issue neither)
  • maybe try to see for a not so expensive online service to handle the middleware for you

At the end, Image or Video is heavy and expensive to process. And doing those things require quite some knowledge too!

kissu
  • 40,416
  • 14
  • 65
  • 133
1

Eventually, I was able to solve my problem without using any package, and what I did was simply convert the image to a canvas and then I converted the image to WEBP format. Below is my solution.

    convertImage(file) {
        return new Promise((resolve) => {
            // convert image
            let src = URL.createObjectURL(file)
            let canvas = document.createElement('canvas');
            let ctx = canvas.getContext('2d')

            let userImage = new Image();
            userImage.src = src

            userImage.onload = function() {
                canvas.width = userImage.width;
                canvas.height = userImage.height;
                ctx.drawImage(userImage, 0, 0);

                let webpImage = canvas.toDataURL("image/webp");
                return resolve(webpImage);
            }
        });    
    },

so, the function above first receives a file which is the image you want to convert from file input, then it converts the image into a canvas then converts the canvas back into an image, but this time you specify the format of the image you want to convert it into.

Since in my case, I wanted a webp image, I set canvas.toDataURL("image/webp") and by default, the quality of the WEBP image will be the same quality as the image that is received. if you want to reduce the quality to lower quality, the canvas.toDataURL("image/webp", 1) takes another argument which is a number between 0-1, 1 for the highest quality, and 0 lowest quality. you could set 0.5 for medium quality too, or whatever you want. You could also set other file formats you want through the first argument like canvas.toDataURL('image/jpeg', 1.0)-- for jpeg format or canvas.toDataURL('image/png', 1.0)--for png.

sources

the small channel where I found my solution - Where I found my solution

developer.mozilla.org explanation - more on the CanvasElement.toDataURL()

Opeyemi Odedeyi
  • 766
  • 1
  • 10
  • 38