I was trying to compress an image from file input using canvas.
I read answers here, some say canvas makes the file larger, some use it to compress images, and since I am new to this :
function compressImg(img){
var canvas=document.createElement("canvas");
var ctx=canvas.getContext("2d");
canvas.width=img.width/2;
canvas.height=img.height/2;
ctx.drawImage(img,0,0,canvas.width,canvas.height);
document.getElementById("imagePreview0").src = canvas.toDataURL();
const data = ctx.canvas.toDataURL(img, "image/jpeg", 0.1);
console.log("size2",data.length);
}
var img = new Image();
img.onload = function(){
console.log("size1",file.length);
compressImg(img);
};
img.src = file;
for an image that the mac says 1.7M , i get
size1 2,318,839
size2 4,702,282.
So - can you really compress an image ?
I just need to reduce file size to under 2M .
Change the below line:
const data = ctx.canvas.toDataURL(img, "image/jpeg", 0.1);
To:
const data = canvas.toDataURL("image/jpeg", 0.1);
Related
I want to create a black (or white, I really don't care the "colour") 1x1px image in javascript, I tried doing it with var img = new Image(1,1) but I don't know how assigng the source of the image with img.src to be all black (I don't want to use a file, I want to be generated with JS code).
There is a way to do that?
You can also use base64 encoded data in the image.src.
var image = new Image(10, 10);
image.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=";
document.body.appendChild(image);
I found it:
var image = document.createElement('canvas').getContext('2d').getImageData(0,0,1,1);
I'll let question open to know if there is another way to do it.
Sources I used:
CanvasRenderingContext2D.createImageData()
CanvasRenderingContext2D.getImageData()
Similarly you can perform the image manipulation in canvas, then pass a data URI containing a representation of the image over to the HTML image for actual rendering.
const imgWidth = 1;
const canvas = document.createElement("canvas");
canvas.width = imgWidth;
canvas.height = imgWidth;
const ctx = canvas.getContext('2d');
ctx.fillStyle = 'black';
ctx.fillRect(0,0,imgWidth,imgWidth);
const url = canvas.toDataURL();
const newImg = document.createElement("img");
newImg.src = url;
document.body.insertBefore(newImg, null);
I'm uploading an image file (size 368KB) and reading it as a 64 bit data url. The size of the data url is 501999 bytes. When I now paint it on a canvas and try to find the size of the data url of the canvas, I'm getting a size of 1091506 bytes.
I can expect some increase after painting it on canvas, but an image blowing to twice its size is pretty weird. If I use an image of around 230KB, the image blows up to more than 8-9 times its original size. However, if I use an image of around 50KB, the increase in size is only marginal.
What's the reason?
Stackblitz is here
let uploadElement = document.getElementById('upload')
uploadElement.onchange = onImageChange;
function onImageChange(){
let inputFile = event.target.files[0];
let fileReader = new FileReader();
fileReader.onload = function(event){
let imageAsBase64 = event.target.result;
let initialSize = imageAsBase64.length;
console.log(initialSize);
const tempImage = new Image();// <HTMLImageElement>document.getElementById('test');
tempImage.src = imageAsBase64;
tempImage.onload = () => {
const element = document.createElement('canvas');
element.width = tempImage.width;
element.height = tempImage.height;
const ctx = element.getContext('2d');
ctx.drawImage(tempImage, 0, 0, element.width, element.height);
let endSize = element.toDataURL().length;
console.log(endSize);
}
}
fileReader.readAsDataURL(inputFile);
}
<input type="file" id="upload" name="image"/>
What's the reason?
One of the reasons is: You're trying to upload a jpg image, but when you're trying to make a copied version by:
const tempImage = new Image();
it really makes a new image with png format. You can check by downloading that copied version:
document.body.appendChild(tempImage);
That's why you have different sizes.
I have seen many questions and solutions for this now. I am new to Mongo DB and MEAN stack development. I want to know whether there is anyway to store image content itself rather than path of the image file in Mongo DB. All the solutions suggests to store image as buffer and then use it back in the source by converting buffer to base64. I did it but the resulting output get resolves to path to the image file rather than the image content. I am looking to save image itself in DB.
// saving image
var pic = {name : "profilePicture.png",
img : "images/default-profile-pic.png",
contentType : "image/png"
};
//schema
profilePic:{ name: String, img: Buffer, contentType: String }
//retrieving back
var base64 = "";
var bytes = new Uint8Array( profilePic.img.data );
var len = bytes.byteLength;
for (var i = 0; i < len; i++) {
base64 += String.fromCharCode( bytes[ i ] );
}
var proPic = "data:image/png;base64," + base64;
console.log(proPic);
//console output
data:image/png;base64,images/default-profile-pic.png
The output for proPic resolves to "data:image/png;base64,images/default-profile-pic.png"
few links that I referred before posting this
How to do Base64 encoding in node.js?
How to convert image into base64 string using javascript
The problem is simply, that you don't read and encode the picture. Instead you use the path as a string.
Serverside using Node
If you want to perform it on the serverside with an image on the filesystem you can use something along following:
var fs = require('fs');
// read and convert the file
var bitmap = fs.readFileSync("images/default-profile-pic.png");
var encImage = new Buffer(bitmap).toString('base64');
// saving image
var pic = {name : "profilePicture.png",
img : encImage,
contentType : "image/png"
};
....
Clientside
Again we need to load the image and encode it as base64. There is an answer about doing this on the client here.
using the first approach the result would be something like following:
function toDataUrl(url, callback, outputFormat){
var img = new Image();
img.crossOrigin = 'Anonymous';
img.onload = function(){
var canvas = document.createElement('CANVAS');
var ctx = canvas.getContext('2d');
var dataURL;
canvas.height = this.height;
canvas.width = this.width;
ctx.drawImage(this, 0, 0);
dataURL = canvas.toDataURL(outputFormat);
callback(dataURL);
canvas = null;
};
img.src = url;
}
toDataUrl("images/default-profile-pic.png", function(encImage){
// saving image
var pic = {name : "profilePicture.png",
img : encImage,
contentType : "image/png"
};
//Proceed in the callback or use a method to pull out the data
....
});
Below two links saved my time. If we use "ng-file-upload" our life becomes easy from there.
https://github.com/danialfarid/ng-file-upload#install
https://github.com/danialfarid/ng-file-upload
Below is what worked for me
//my html code
<div>
<button type="file" ngf-select="onFileSelect($file)" ng-model="file" name="file" ngf-pattern="'image/*'"
ngf-accept="'image/*'" ngf-max-size="15MB" class="btn btn-danger">
Edit Profile Picture</button>
</div>
//my js function
function onFileSelect(file){
//var image = document.getElementById('uploadPic').files;
image = file;
if (image.type !== 'image/png' && image.type !== 'image/jpeg') {
alert('Only PNG and JPEG are accepted.');
return;
}
$scope.uploadInProgress = true;
$scope.uploadProgress = 0;
var reader = new window.FileReader();
reader.readAsDataURL(image);
reader.onloadend = function() {
base64data = reader.result;
$scope.profile.profilePic = base64data;
ProfileService.updateProfile($scope.profile).then(function(response){
$rootScope.profile = response;
$scope.profilePicture = $rootScope.profile.profilePic;
});
}
}
// when reading from the server just put the profile.profilePic value to src
src="data:image/png;base64,{base64 string}"
// profile schema
var ProfileSchema = new mongoose.Schema({
userid:String,
//profilePic:{ name: String, img: Buffer, contentType: String },
profilePic:String
}
I wouldn't say this is the best solution but a good place to start.Also this limits you from uploading file size more than 16 MB in which case you can use"GridFs" in the above implementation initially the file is converted to "blob" and then I am converting it to "base64" format and adding that to my profile's string variable.
Hope this helps someone in saving their time.
An image is loaded to the fabric canvas using file input and FileReader. We use scaleToWidth and scaleToHeight to have large photos fit to the canvas.
When I use choose a large 3.2MB jpeg the image is nicely resized to 1MB which is what we want. We then prepare the data for storage on local indexed db;
canvas.toJSON(); // 4.2MB
canvas.toDataURL(); // 1MB
It seems that the toJSON method stores the original jpeg. Can we reduce the jpeg prior to serialization ?
I'd prefer to serialize to JSON so we can use other excellent Fabric features in the future.
we have this figured by ;
loading the photo to Fabric.js canvas
then exporting it (which reduces the image to the canvas dimensions) and
reloading reduced image data back on the canvas and
removing the original full size photo.
Now the fabric.js canvas data is nicely reduced for storage in local indexeddb;
// camera image // 3.2 MB
canvas.toJSON(); // 1 MB
canvas.toDataURL(); // 1 MB
javascript
var reader = new FileReader();
reader.onload = function (event) {
var img = new Image();
var opts = {};
img.onload = function () {
var imgInstance = new fabric.Image(img, opts);
if (imgInstance.getWidth() > canvas.getWidth()) {
imgInstance.scaleToWidth(canvas.getWidth());
}
if (imgInstance.getHeight() > canvas.getHeight()) {
imgInstance.scaleToHeight(canvas.getHeight());
}
canvas.add(imgInstance);
canvas.renderAll();
img = null;
/* now that the image is loaded reduce it's size
so the original large image is not stored */
/* assumes photo is object 0, need to code a function to
find the index otherwise */
var photoObjectIx = 0;
var originalPhotoObject = canvas.getObjects()[photoObjectIx];
var nimg = new Image();
nimg.onload = function () {
var imgInstance = new fabric.Image(nimg, { selectable: false });
canvas.remove(originalPhotoObject);
canvas.add(imgInstance);
canvas.renderAll();
nimg = null;
};
nimg.src = originalPhotoObject.toDataURL();
}
img.src = event.target.result;
}
reader.readAsDataURL(e.target.files[0]);
I personally compress big json data and then decompress it on the server...
Deflate in JS - nice script to gzdeflate (compress) JSON.
and then... in PHP:
<?php
$json = gzinflate($HTTP_RAW_POST_DATA);
?>
Is there any technique to convert images that have already been downloaded – inline JPEG/GIF/etc. images that occur in a webpage – into Base64 data using client-side JavaScript?
I am not talking about how to transform an image into Base64 using other means (server-side, online tools, etc.).
These are the constraints for my particular use case:
The image is on screen now, right in the page, in front of person. It has already been downloaded in a data sense.
The conversion from raw image data has to be done client-side.
The images in question are from arbitrary domains. That is, they may or may not, be of same origin domain.
The user, if needed (if helpful to solution), can give additional permissions (for example, a FF toolbar install to help skirt cross-domain and other issues). That is, code can be given special endorsement on the client side if that helps solve the issue.
The end goal is to transform all images on the page (in the DOM) into Base64 data inside of JavaScript. Put another way, every image the user can see on the page has been converted into a JavaScript variable of some sort that contains the Base64 data.
So far I see no posts that stay inside of all the above constraints.
I think this is close to what you are looking for but the only problem is that it only works for locally hosted images and HTML5 only.
function toURL(image) {
var canvas = document.createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
var context = canvas.getContext("2d");
context.drawImage(image, 0, 0);
var s = canvas.toDataURL();
return s.substring(s.indexOf(","));
}
var test = document.getElementById("myImage");
console.log(toURL(test));
You can trick javascript into thinking an image is from your domain with the following code.
image.php
<?php
$image = getAnImagePathAndTypeFromTheDatabaseByID($_GET["id"]);
//returns something like
//array("path" => "http://www.anotherwebsite.com/image.png", "type" => "png")
header("Content-type: image/$image[type]");
echo file_get_contents($image["path"]);
?>
Then just navigate to image.php?id=1 for example.
For it to work in cross-domain client-side you need to call the image with the attribute crossorigin = "true", or, add a line in the Logan Murphy code:
function toURL(image) {
image.setAttribute('crossOrigin', 'anonymous');
var canvas = document.createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
var context = canvas.getContext("2d");
context.drawImage(image, 0, 0);
var s = canvas.toDataURL();
return s.substring(s.indexOf(","));
}
I use this code:
// image-to-uri.js v1
// converts a URL of an image into a dataURI
function imageToURI(url, callback) {
// Create an empty canvas and image elements
let canvas = document.createElement('canvas');
let img = document.createElement('img');
img.onload = function () {
let ctx = canvas.getContext('2d');
// match size of image
canvas.width = img.naturalWidth || img.width;
canvas.height = img.naturalHeight || img.height;
// Copy the image contents to the canvas
ctx.drawImage(img, 0, 0);
// Get the data-URI formatted image
callback(null, canvas.toDataURL('image/png'));
};
img.ononerror = function () {
callback(new Error('FailedToLoadImage'));
};
// canvas is not supported
if (!canvas.getContext) {
setTimeout(callback, 0, new Error('CanvasIsNotSupported'));
} else {
img.setAttribute('crossOrigin', 'anonymous');
img.src = url;
};
};
which is based on this https://github.com/HenrikJoreteg/image-to-data-uri.js/blob/master/image-to-data-uri.js