The simplest way is to use a canvas element and then invoke a download action allowing the user to select where to save the image.
You mention that the image is large, but not how much - be aware that with canvas you will also run into restrictions when the image source starts to touch around the 8k area in pixel size.
A simplified example (IE will require a polyfill for toBlob()).:
- Load image source via input
- Use File blob directly as image source via
URL.createObjectURL() - When loaded, create a temporary canvas, set canvas size = image size and draw in image
- Use
toBlob()(more efficient on memory and performance and require no transcoding to/from Base64) to obtain aBlob. - We'll convert the
BlobtoFile(a subset object and it will reference the same memory) so we can also give a filename as well as (important!) a binary mime-type.
Since the mime-type for the final step is binary the browser will invoke a Save as dialog.
document.querySelector("input").onchange = function() {
var img = new Image;
img.onload = convert;
img.src = URL.createObjectURL(this.files[0]);
};
function convert() {
URL.revokeObjectURL(this.src); // free up memory
var c = document.createElement("canvas"), // create a temp. canvas
ctx = c.getContext("2d");
c.width = this.width; // set size = image, draw
c.height = this.height;
ctx.drawImage(this, 0, 0);
// convert to File object, NOTE: we're using binary mime-type for the final Blob/File
c.toBlob(function(blob) {
var file = new File([blob], "MyJPEG.jpg", {type: "application/octet-stream"});
window.location = URL.createObjectURL(file);
}, "image/jpeg", 0.75); // mime=JPEG, quality=0.75
}
// NOTE: toBlob() is not supported in IE, use a polyfill for IE.
<label>Select image to convert: <input type=file></label>
Update: If you just are after a string (base-64 encoded) version of the newly created JPEG simply use toDataURL() instead of toBlob():
document.querySelector("input").onchange = function() {
var img = new Image;
img.onload = convert;
img.src = URL.createObjectURL(this.files[0]);
};
function convert() {
URL.revokeObjectURL(this.src); // free up memory
var c = document.createElement("canvas"), // create a temp. canvas
ctx = c.getContext("2d");
c.width = this.width; // set size = image, draw
c.height = this.height;
ctx.drawImage(this, 0, 0);
// convert to File object, NOTE: we're using binary mime-type for the final Blob/File
var jpeg = c.toDataURL("image/jpeg", 0.75); // mime=JPEG, quality=0.75
console.log(jpeg.length)
}
<label>Select image to convert: <input type=file></label>
Answer from user1693593 on Stack OverflowThe simplest way is to use a canvas element and then invoke a download action allowing the user to select where to save the image.
You mention that the image is large, but not how much - be aware that with canvas you will also run into restrictions when the image source starts to touch around the 8k area in pixel size.
A simplified example (IE will require a polyfill for toBlob()).:
- Load image source via input
- Use File blob directly as image source via
URL.createObjectURL() - When loaded, create a temporary canvas, set canvas size = image size and draw in image
- Use
toBlob()(more efficient on memory and performance and require no transcoding to/from Base64) to obtain aBlob. - We'll convert the
BlobtoFile(a subset object and it will reference the same memory) so we can also give a filename as well as (important!) a binary mime-type.
Since the mime-type for the final step is binary the browser will invoke a Save as dialog.
document.querySelector("input").onchange = function() {
var img = new Image;
img.onload = convert;
img.src = URL.createObjectURL(this.files[0]);
};
function convert() {
URL.revokeObjectURL(this.src); // free up memory
var c = document.createElement("canvas"), // create a temp. canvas
ctx = c.getContext("2d");
c.width = this.width; // set size = image, draw
c.height = this.height;
ctx.drawImage(this, 0, 0);
// convert to File object, NOTE: we're using binary mime-type for the final Blob/File
c.toBlob(function(blob) {
var file = new File([blob], "MyJPEG.jpg", {type: "application/octet-stream"});
window.location = URL.createObjectURL(file);
}, "image/jpeg", 0.75); // mime=JPEG, quality=0.75
}
// NOTE: toBlob() is not supported in IE, use a polyfill for IE.
<label>Select image to convert: <input type=file></label>
Update: If you just are after a string (base-64 encoded) version of the newly created JPEG simply use toDataURL() instead of toBlob():
document.querySelector("input").onchange = function() {
var img = new Image;
img.onload = convert;
img.src = URL.createObjectURL(this.files[0]);
};
function convert() {
URL.revokeObjectURL(this.src); // free up memory
var c = document.createElement("canvas"), // create a temp. canvas
ctx = c.getContext("2d");
c.width = this.width; // set size = image, draw
c.height = this.height;
ctx.drawImage(this, 0, 0);
// convert to File object, NOTE: we're using binary mime-type for the final Blob/File
var jpeg = c.toDataURL("image/jpeg", 0.75); // mime=JPEG, quality=0.75
console.log(jpeg.length)
}
<label>Select image to convert: <input type=file></label>
JavaScript on the client side can not save files. You have multiple options:
- Render the image on an
<canvas>element. This way it can be saved with right click -> save image - Inset the image as
<img>element. This way it can be saved with right click -> save image - Send the image data as base64 string to the server. Do the processing there
- Use a server side language like PHP or Node.js to save the file
Long story short, you have to use some server side logic to save the file on disk
How to convert several image into JPG?
- Install Aspose.Words for Node.js via .NET.
- Add a library reference (import the library) to your JavaScript project.
- Open the source image file in JavaScript.
- Convert several image files into JPG in a few seconds.
- Call the appendDocument() method, passing an output filename with JPG extension.
- Get the result of conversion image into JPG.
What output file formats can I use to merge image files?
What is the maximum image size supported by Aspose.Words for Node.js via .NET?
Cause
The reason for this to happen is due to canvas being transparent. However the transparancy color is black with a transparent alpha-channel so it won't show on screen.
JPEG on the other side doesn't support alpha-channel so that black color which is the default is stripped of its alpha channel leaving a black background.
You PNG supports alpha-channel so it is compatible with the way canvas work.
Solution
To get around this you will have to manually draw in a white background on the canvas before you draw in the image:
var canvas = $('.jSignature')[0];
var ctx = canvas.getContext('2d');
ctx.fillStyle = '#fff'; /// set white fill style
ctx.fillRect(0, 0, canvas.width, canvas.height);
/// draw image and then use toDataURL() here
Just as an alternative way, using a package to convert black background of transparent image to white or any other other based on the provided HEX value, in our
const Jimp = require("jimp");
// Read the PNG file and convert it to editable format
Jimp.read("./images/your-image.png", function (err, image) {
if (err) {
// Return if any error
console.log(err);
return;
}
image.background(0xFFFFFFFF, (err, val) => {
// Convert image to JPG and store it to
// './output/' folder with 'out.jpg' name
image.write("./output/out.jpg");
})
});
If we have a look at the source from the JPG to PNG website which uses pure javascript to convert images from JPG to PNG. We see that they:
- Load the jpg image from file
- Create a canvas of the same size as the jpg
- Draw the jpg image covering the whole canvas
- Convert canvas to blob (if the image is small enough you can also use
.toDataURL()) - Download the blob
There are plenty ports of native png/JPEG libraries through emscripten and also a couple written purely in JavaScript, This is what it comes to my mind now:
https://www.npmjs.com/package/jimp
Jimp.read('lenna.png', (err, lenna) => {
if (err) throw err;
lenna
.write('lena-small-bw.jpg'); // save
});
But in general you want to search something like 'png to jpeg' in npm.org you will find plenty libraries.
tl;dr: Don't bother
This might be theoretically possible, but I would recommend against it very strongly. It's possible to create a javascript Img element which refers to an URL the user has typed in. You can then draw this image in an HTML5 canvas.
You can then manually access the data on the canvas and analyze/convert the image to the approriate format. It might then be possible to send this Base64 or URL-encode to a server which could then return the image to the client.
This is of course completely crazy and should NOT be attempted. This solution would require implementing JPG compression in javascript which, although technically possible, is probably not feasible because of browser constraints (eg. speed).
Modern browser now provide the methods to convert images in the browser, if this is desired. The following method can be used on any browser that supports OffscreenCanvas and the required file types:
/**
* Convert between any image formats the browser supports
* @param {Blob} source A Blob (or File) containing the image to convert
* @param {string} type The MIME type of the target format
* @returns {Promise<Blob>} The converted image
*/
async function convert(source, type) {
let image = await createImageBitmap(source);
let canvas = new OffscreenCanvas(image.width, image.height);
let context = canvas.getContext("2d");
context.drawImage(image, 0, 0);
let result = await canvas.convertToBlob({ type });
image.close();
return result;
}
On browsers that don't support OffscreenCanvas, such as (as of January 2023) Safari, the following method can be used:
/**
* Convert between any image formats the browser supports
* @param {Blob} source A Blob (or File) containing the image to convert
* @param {string} type The MIME type of the target format
* @returns {Promise<Blob>} The converted image
*/
async function convertLegacy(source, type) {
let image = await createImageBitmap(source);
let canvas = document.createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
let context = canvas.getContext("2d");
context.drawImage(image, 0, 0);
let result = await new Promise((resolve, reject) => {
canvas.toBlob((blob) => {
if (blob != null) {
resolve(blob);
} else {
reject(new Error("Failed to convert file"));
}
}, type, 1);
});
image.close();
return result;
}
Note that the second method won't work in a WebWorker.
If an unsupported target format is given, the browser will default to PNG. There are probably no browser without JPEG support.