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?
you could use the packages imagemin and imagemin-webp as answered here: Convert Images to webp with Node
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!
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()
Usually I import png images into Canvas by using
const image = Canvas.LoadImage('url.png')
const canvas = Canvas.createCanvas(256,256)
const ctx = canvas.getContext('2d')
ctx.drawImage(image,256,256)
but when i try to import a webp image, I get an error saying that webp isn't supported. On research into the Issues of node-canvas I found this issue, The importing issue appears to be solved but I dont understand how to import the webp images now.
I tried using the Image, ImageFromBuffer(await fetch(url).buffer()) from the fix in the issue but both give errors.
I solved it by using the library Sharp.
First get the file as a buffer
// axios for remote images- maybe fs for local images?
const imageResponse = await axios.get(url, {
responseType: 'arraybuffer',
});
Convert the file to png using sharp:
const img = await sharp(imageResponse.data).toFormat('png').toBuffer();
Then you can use loadImage
const file = await loadImage(img).then((image) => {
ctx.drawImage(
image,
256,
256
);
return { buffer: canvas.toBuffer(), mimetype: `image/png` };
});
I'm trying to extract EXIF data from images using ImageMagick inside AWS Lambda but I can't find the way to do it.
I have a piece of code to resize the image, it's working fine but I want to add the part to extract EXIF data.
Here is what I have right now to resize images:
var im = require("gm").subClass({imageMagick: true});
var operation = im(image.buffer).autoOrient().resize(width, height, '^');
operation.toBuffer(image.imageType, function(err, buffer) {
if (err) {
//do something with the error
} else {
//do something with the image
}
});
Any idea how to extract the metadata from the image ?
Thanks.
C.C.
I'm writing a nodeJS 5.3.0 application using gm (http://aheckmann.github.io/gm/)
I know that it uses the GraphicsMagicK library.
the problem is that I'm having is that after I resize an image, it loses it's exif format. the code samples actually shows that the exif format is lost.
for example:
var fs = require('fs')
, gm = require('gm').subClass({imageMagick: true});
// resize and remove EXIF profile data
gm('/path/to/my/img.jpg')
.resize(240, 240)
in this example they say that exif profile data is removed.
I know that I can get the orientation of an image before resizing using:
gm('path/tp/my/img.jpg').orientation(function(err,value){
var orientation = value;
});
the question is..
can I preserve exif data when resizing ? and If not.. can I set exif orientation data after resizing ?
thanks
More specifically in the following code, only noProfile() function remove EXIF, so if you remove it you can preserve EXIF data
// resize and remove EXIF profile data
gm('/path/to/my/img.jpg')
.resize(240, 240)
.noProfile()
.write('/path/to/resize.png', function (err) {
if (!err) console.log('done');
});
Otherwise you can check the gm doc here
I'm writing an API endpoint to accept images and send them to my S3 server. Before that, however, it has to orient the images correctly so that not display wrong when in image tags. Since JPEGs use exif data to determine image rotation and the img tag does not refer to the exif data for orientation information, my images appear sideways on my website, but right side up on other platforms. Here is my code for rotating the image:
if (file.mimetype == 'image/jpeg'){
var exifData = Parser.create(uploadedFile).parse();
if (exifData.tags.Orientation == 6 || exifData.tags.Orientation == 8){
var degrees = exifData.tags.Orientation == 6 ? 90 : 270;
gm(file.path).rotate('black', degrees).write(file.path, function(err){
if (err)
return cb1(err);
finish();
});
}else
finish();
}else
finish();
finish() is the function that sends the file off to S3. Why is it that my image is not rotating? I have it set to overwrite the existing file on rotate. There are no errors being returned, as the entire call would fail.