I know this might probably been answered many times but I do not know the words to search for to find the answer. So here is the question.
I'm using javascript and I have
function getDataUrl( file ){
var url = URL.createObjectURL( file ),
canvas = document.createElement('canvas'),
ctx = canvas.getContext("2d"),
img = new Image,
dataURL = '';
img.load = function(){
canvas.height = this.height;
canvas.width = this.width;
ctx.drawImage(this, 0, 0);
dataURL = canvas.toDataURL("image/jpeg");
};
img.src = url;
return dataURL;
}
then on the input I have this,
$('#file-input').on('change', function(e){
var dataUrl = getDataUrl( e.target.files[0] );
console.log( dataUrl );
});
But it return empty string (the initial one). I want the base64 data that already generated inside load.
I've try something like this
function getDataUrl( file ){
//... codes
img.load = function(){
//... codes
dataURL = canvas.toDataURL("image/jpeg");
return dataUrl;
};
}
but of course it can't since it return to the load function and not to it's parent function.
How do I get dataUrl value inside load function ?
Thanks
Your code, abbreviated, looks like this:
function getDataUrl() {
dataURL = '';
img.load = function() {
dataURL = 'Hello!';
};
return dataURL;
}
What img.load = function does is nothing except assign a function to img.load, the execution will then just continue with the return statement, meaning that your dataURL variable retains the original value.
You should realize that functions in Javascript van be assigned to variables just like a string or number can. It's doesn't need to be executed.
It may be a bit confusing, it's certainly different from many other languages, but you'll get the hang of it. Be sure to continue reading & learning about javascript.
You probably want to use img.onload, this will execute the function whenever the image is finished loading. But this can be whenever, maybe 1 second, maybe 10, maybe never.
What you need to do, is something like:
$('#file-input').on('change', function(e) {
getDataUrl(e.target.files[0], function(dataUrl) {
console.log(dataUrl);
});
});
function getDataUrl(callback) {
dataURL = '';
img.onload = function() {
callback(dataUrl);
};
return dataURL;
}
Here you pass a function as an argument, an execute that function once the image finished loading, passing dataUrl as a parameter. This is a fairly common pattern in Javascript.
The load event is asynchronous. That means it occurs sometime in the future AFTER your getDataURL() function has completed. Thus, you cannot return the value you want from the getDataURL() function because the image has not loaded yet and the load event handler has not yet been called. So, instead, you need to think asynchronously and program in a way that deals with that. The typical way of doing this is to pass in a callback function that will get called when the data is available and you can pass that data to the callback function.
There are many different ways to code this, but here's one way:
function getDataUrl( file, callback ){
var url = URL.createObjectURL( file ),
canvas = document.createElement('canvas'),
ctx = canvas.getContext("2d"),
img = new Image,
dataURL = '';
img.load = function(){
canvas.height = this.height;
canvas.width = this.width;
ctx.drawImage(this, 0, 0);
dataURL = canvas.toDataURL("image/jpeg");
// pass the dataURL to the callback function
callback(dataURL);
};
img.src = url;
}
getDataUrl("myfile.jpg", function(dataURL) {
// code here to use the dataURL
});
Related
A lot of reference I see about this problem is about upload file and convert to base64 but in my case I want to convert an Image URL from server and convert it to base64 but I still failed to do it, right now I tried it like this, but it still failed since it doesn't show anything
this is my html:
<div v-if="questionData">
<img class="img-preview-download" :src="questionData.image_url? getBase64Image(questionData.image_url) : 'https://via.placeholder.com/640x360'" alt="img-preview">
</div>
this is my method:
getBase64Image(img) {
console.log("cek base64 : ", btoa(img));
return `data:image/jpeg;base64,${btoa(img)}`;
},
I read some using file reader but isn't it only for file when you upload a data using input? can someone help me to solve this? I'm using Vue.Js for the framework
when I used this method I got result like this:
So this is my answer for my future self, who might be forget and stumble again in this problem!
You can solve it by making a new image and inside that image file, you can add your src so the image can be process when still loading or onload.
Remember!
Since it is you, You might be remove the last image.src = url to get a clean code, but this is important, if you remove that line, image.onload will not be trigger because it will search for the image source. and if you try to use image.srcObject to put it with mediaStream it will give you Resolution Overloaded since you still not find the answer for this problem, it is okay, you can use the image first since your step is to achieve how to get file from Image URL. so this is the method you use to solve this problem:
downloadPreview() {
const el = this.$refs.printMe;
const options = {
type: 'dataURL'
};
this.$html2canvas(el, options).then(data => {
this.output = data;
const a = document.createElement('a');
a.style.display = 'none';
a.href = data;
// this is just optional function to download your file
a.download = `name.jpeg`;
document.body.appendChild(a);
a.click();
});
},
convertImgUrlToBase64(url) {
let self = this;
var image = new Image();
image.setAttribute('crossOrigin', 'anonymous'); // use it if you try in a different origin of your web
image.onload = function () {
var canvas = document.createElement('canvas');
canvas.width = image.width;
canvas.height = image.height;
canvas.getContext('2d').drawImage(this, 0, 0);
canvas.toBlob(
function(source) {
var newImg = document.createElement("img"),
url = URL.createObjectURL(source);
newImg.onload = function() {
// no longer need to read the blob so it's revoked
URL.revokeObjectURL(url);
};
newImg.src = url;
},
"image/jpeg",
1
);
// If you ever stumble at 18 DOM Exception, just use this code to fix it
// let dataUrl = canvas.toDataURL("image/jpeg").replace("image/jpeg", "image/octet-stream");
let dataUrl = canvas.toDataURL("image/jpeg");
console.log("cek inside url : ", url);
if(url === backgroundImg) {
self.assignImageBase64Background(dataUrl);
} else {
self.assignImageBase64(dataUrl);
}
};
image.src = url;
},
assignImageBase64(img) {
this.imgBase64 = img;
},
just for information, I use this library to change the div into image file:
vue-html2canvas
Notes:
If you ever wondering why I give self.assignImageBase64(dataUrl); this function in the end, this is because I still wondering how onload works, and how to return Base64 url to the parent thats why I just assign it again in another function since it easier to do.
I have a regular HTML page with some images (just regular <img /> HTML tags). I'd like to get their content, base64 encoded preferably, without the need to redownload the image (ie. it's already loaded by the browser, so now I want the content).
I'd love to achieve that with Greasemonkey and Firefox.
Note: This only works if the image is from the same domain as the page, or has the crossOrigin="anonymous" attribute and the server supports CORS. It's also not going to give you the original file, but a re-encoded version. If you need the result to be identical to the original, see Kaiido's answer.
You will need to create a canvas element with the correct dimensions and copy the image data with the drawImage function. Then you can use the toDataURL function to get a data: url that has the base-64 encoded image. Note that the image must be fully loaded, or you'll just get back an empty (black, transparent) image.
It would be something like this. I've never written a Greasemonkey script, so you might need to adjust the code to run in that environment.
function getBase64Image(img) {
// Create an empty canvas element
var canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
// Copy the image contents to the canvas
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
// Get the data-URL formatted image
// Firefox supports PNG and JPEG. You could check img.src to
// guess the original format, but be aware the using "image/jpg"
// will re-encode the image.
var dataURL = canvas.toDataURL("image/png");
return dataURL.replace(/^data:image\/(png|jpg);base64,/, "");
}
Getting a JPEG-formatted image doesn't work on older versions (around 3.5) of Firefox, so if you want to support that, you'll need to check the compatibility. If the encoding is not supported, it will default to "image/png".
This Function takes the URL then returns the image BASE64
function getBase64FromImageUrl(url) {
var img = new Image();
img.setAttribute('crossOrigin', 'anonymous');
img.onload = function () {
var canvas = document.createElement("canvas");
canvas.width =this.width;
canvas.height =this.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(this, 0, 0);
var dataURL = canvas.toDataURL("image/png");
alert(dataURL.replace(/^data:image\/(png|jpg);base64,/, ""));
};
img.src = url;
}
Call it like this :
getBase64FromImageUrl("images/slbltxt.png")
Coming long after, but none of the answers here are entirely correct.
When drawn on a canvas, the passed image is uncompressed + all pre-multiplied.
When exported, its uncompressed or recompressed with a different algorithm, and un-multiplied.
All browsers and devices will have different rounding errors happening in this process
(see Canvas fingerprinting).
So if one wants a base64 version of an image file, they have to request it again (most of the time it will come from cache) but this time as a Blob.
Then you can use a FileReader to read it either as an ArrayBuffer, or as a dataURL.
function toDataURL(url, callback){
var xhr = new XMLHttpRequest();
xhr.open('get', url);
xhr.responseType = 'blob';
xhr.onload = function(){
var fr = new FileReader();
fr.onload = function(){
callback(this.result);
};
fr.readAsDataURL(xhr.response); // async call
};
xhr.send();
}
toDataURL(myImage.src, function(dataURL){
result.src = dataURL;
// now just to show that passing to a canvas doesn't hold the same results
var canvas = document.createElement('canvas');
canvas.width = myImage.naturalWidth;
canvas.height = myImage.naturalHeight;
canvas.getContext('2d').drawImage(myImage, 0,0);
console.log(canvas.toDataURL() === dataURL); // false - not same data
});
<img id="myImage" src="https://dl.dropboxusercontent.com/s/4e90e48s5vtmfbd/aaa.png" crossOrigin="anonymous">
<img id="result">
A more modern version of kaiido's answer using fetch would be:
function toObjectUrl(url) {
return fetch(url)
.then((response)=> {
return response.blob();
})
.then(blob=> {
return URL.createObjectURL(blob);
});
}
https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
Edit: As pointed out in the comments this will return an object url which points to a file in your local system instead of an actual DataURL so depending on your use case this might not be what you need.
You can look at the following answer to use fetch and an actual dataURL: https://stackoverflow.com/a/50463054/599602
shiv / shim / sham
If your image(s) are already loaded (or not), this "tool" may come in handy:
Object.defineProperty
(
HTMLImageElement.prototype,'toDataURL',
{enumerable:false,configurable:false,writable:false,value:function(m,q)
{
let c=document.createElement('canvas');
c.width=this.naturalWidth; c.height=this.naturalHeight;
c.getContext('2d').drawImage(this,0,0); return c.toDataURL(m,q);
}}
);
.. but why?
This has the advantage of using the "already loaded" image data, so no extra request is needed. Additionally it lets the end-user (programmer like you) decide the CORS and/or mime-type and quality -OR- you can leave out these arguments/parameters as described in the MDN specification here.
If you have this JS loaded (prior to when it's needed), then converting to dataURL is as simple as:
examples
HTML
<img src="/yo.jpg" onload="console.log(this.toDataURL('image/jpeg'))">
JS
console.log(document.getElementById("someImgID").toDataURL());
GPU fingerprinting
If you are concerned about the "preciseness" of the bits then you can alter this tool to suit your needs as provided by #Kaiido's answer.
its 2022, I prefer to use modern createImageBitmap() instead of onload event.
*note: image should be same origin or CORS enabled
async function imageToDataURL(imageUrl) {
let img = await fetch(imageUrl);
img = await img.blob();
let bitmap = await createImageBitmap(img);
let canvas = document.createElement("canvas");
let ctx = canvas.getContext("2d");
canvas.width = bitmap.width;
canvas.height = bitmap.height;
ctx.drawImage(bitmap, 0, 0, bitmap.width, bitmap.height);
return canvas.toDataURL("image/png");
// image compression?
// return canvas.toDataURL("image/png", 0.9);
};
(async() => {
let dataUrl = await imageToDataURL('https://en.wikipedia.org/static/images/project-logos/enwiki.png')
wikiImg.src = dataUrl;
console.log(dataUrl)
})();
<img id="wikiImg">
Use onload event to convert image after loading
function loaded(img) {
let c = document.createElement('canvas')
c.getContext('2d').drawImage(img, 0, 0)
msg.innerText= c.toDataURL();
}
pre { word-wrap: break-word; width: 500px; white-space: pre-wrap; }
<img onload="loaded(this)" src="https://cors-anywhere.herokuapp.com/http://lorempixel.com/200/140" crossorigin="anonymous"/>
<pre id="msg"></pre>
This is all you need to read.
https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsBinaryString
var height = 200;
var width = 200;
canvas.width = width;
canvas.height = height;
var ctx = canvas.getContext('2d');
ctx.strokeStyle = '#090';
ctx.beginPath();
ctx.arc(width/2, height/2, width/2 - width/10, 0, Math.PI*2);
ctx.stroke();
canvas.toBlob(function (blob) {
//consider blob is your file object
var reader = new FileReader();
reader.onload = function () {
console.log(reader.result);
}
reader.readAsBinaryString(blob);
});
In HTML5 better use this:
{
//...
canvas.width = img.naturalWidth; //img.width;
canvas.height = img.naturalHeight; //img.height;
//...
}
I've a situation where I need to work on a user-provided image with two different functions.
Get user input
Process the image and put it back processed
note: code is incomplete and shortened for brevity. Please don't point out the irrelevant.
1. Get user input
var fReader = new FileReader();
fReader.onload = function(e){
image = new Image();
image.onload = function(){
//BEGIN OF RELEVANT SECTION
processOnCanvasAndBack(image, myCallbackToProceed);
};
image.src = e.target.result;
};
fReader.readAsDataURL(src);
2. Process the image and put it back processed
function processOnCanvasAndBack(image) {
var canvas = $('<canvas></canvas>');
canvas.draw(image);
canvas.doSomeStuffLikeRotatingAndColorBalance();
//BEGIN OF RELEVANT SECTION
image.onload = function() {
myCallbackToProceed();
};
image.src = canvas.toDataURL();
}
Problem
The image.onload from 1. calls the function as expected but when I call the second image.src from 2 the first image.onload gets called again, which in turns calls 2 again and.... booooom, infinite loop (console spits too many recursions)
I tried to reset the first call with image.onload = function(){}; in various points, but it doesn't fix the issue (no more recursion, but the functions just stop being called). Right now I'm out of ideas :-(
I would suggest creating two image objects. One for the source image, and a second for the transformed / target image. You avoid mutating existing state and causing infinite loops by repeatedly setting the .src of the same image object in the onload events. Avoid mutating state whenever possible. I would also suggest using the var keyword to define the variables locally instead of in the global scope.
var fReader = new FileReader();
fReader.onload = function(e){
var sourceImage = new Image();
var targetImage = new Image();
sourceImage.onload = function() {
//BEGIN OF RELEVANT SECTION
processOnCanvasAndBack(sourceImage, targetImage, myCallbackToProceed);
}
sourceImage.src = e.target.result;
};
fReader.readAsDataURL(src);
function processOnCanvasAndBack(sourceImage, targetImage, callback) {
var canvas = $('<canvas></canvas>');
canvas.draw(sourceImage);
canvas.doSomeStuffLikeRotatingAndColorBalance();
// BEGIN OF RELEVANT SECTION
targetImage.onload = function() {
callback();
};
targetImage.src = canvas.toDataURL();
}
I'm trying to load image and put its data into HTML Image element but without success.
var fs = require("fs");
var content = fs.read('logo.png');
After reading content of the file I have to convert it somehow to Image or just print it to canvas. I was trying to conver binary data to Base64 Data URL with the code I've found on Stack.
function base64encode(binary) {
return btoa(unescape(encodeURIComponent(binary)));
}
var base64Data = 'data:image/png;base64,' +base64encode(content);
console.log(base64Data);
Returned Base64 is not valid Data URL. I was trying few more approaches but without success. Do you know the best (shortest) way to achieve that?
This is a rather ridiculous workaround, but it works. Keep in mind that PhantomJS' (1.x ?) canvas is a bit broken. So the canvas.toDataURL function returns largely inflated encodings. The smallest that I found was ironically image/bmp.
function decodeImage(imagePath, type, callback) {
var page = require('webpage').create();
var htmlFile = imagePath+"_temp.html";
fs.write(htmlFile, '<html><body><img src="'+imagePath+'"></body></html>');
var possibleCallback = type;
type = callback ? type : "image/bmp";
callback = callback || possibleCallback;
page.open(htmlFile, function(){
page.evaluate(function(imagePath, type){
var img = document.querySelector("img");
// the following is copied from http://stackoverflow.com/a/934925
var canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
// Copy the image contents to the canvas
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
// Get the data-URL formatted image
// Firefox supports PNG and JPEG. You could check img.src to
// guess the original format, but be aware the using "image/jpg"
// will re-encode the image.
window.dataURL = canvas.toDataURL(type);
}, imagePath, type);
fs.remove(htmlFile);
var dataUrl = page.evaluate(function(){
return window.dataURL;
});
page.close();
callback(dataUrl, type);
});
}
You can call it like this:
decodeImage('logo.png', 'image/png', function(imgB64Data, type){
//console.log(imgB64Data);
console.log(imgB64Data.length);
phantom.exit();
});
or this
decodeImage('logo.png', function(imgB64Data, type){
//console.log(imgB64Data);
console.log(imgB64Data.length);
phantom.exit();
});
I tried several things. I couldn't figure out the encoding of the file as returned by fs.read. I also tried to dynamically load the file into the about:blank DOM through file://-URLs, but that didn't work. I therefore opted to write a local html file to the disk and open it immediately.
I have a regular HTML page with some images (just regular <img /> HTML tags). I'd like to get their content, base64 encoded preferably, without the need to redownload the image (ie. it's already loaded by the browser, so now I want the content).
I'd love to achieve that with Greasemonkey and Firefox.
Note: This only works if the image is from the same domain as the page, or has the crossOrigin="anonymous" attribute and the server supports CORS. It's also not going to give you the original file, but a re-encoded version. If you need the result to be identical to the original, see Kaiido's answer.
You will need to create a canvas element with the correct dimensions and copy the image data with the drawImage function. Then you can use the toDataURL function to get a data: url that has the base-64 encoded image. Note that the image must be fully loaded, or you'll just get back an empty (black, transparent) image.
It would be something like this. I've never written a Greasemonkey script, so you might need to adjust the code to run in that environment.
function getBase64Image(img) {
// Create an empty canvas element
var canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
// Copy the image contents to the canvas
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
// Get the data-URL formatted image
// Firefox supports PNG and JPEG. You could check img.src to
// guess the original format, but be aware the using "image/jpg"
// will re-encode the image.
var dataURL = canvas.toDataURL("image/png");
return dataURL.replace(/^data:image\/(png|jpg);base64,/, "");
}
Getting a JPEG-formatted image doesn't work on older versions (around 3.5) of Firefox, so if you want to support that, you'll need to check the compatibility. If the encoding is not supported, it will default to "image/png".
This Function takes the URL then returns the image BASE64
function getBase64FromImageUrl(url) {
var img = new Image();
img.setAttribute('crossOrigin', 'anonymous');
img.onload = function () {
var canvas = document.createElement("canvas");
canvas.width =this.width;
canvas.height =this.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(this, 0, 0);
var dataURL = canvas.toDataURL("image/png");
alert(dataURL.replace(/^data:image\/(png|jpg);base64,/, ""));
};
img.src = url;
}
Call it like this :
getBase64FromImageUrl("images/slbltxt.png")
Coming long after, but none of the answers here are entirely correct.
When drawn on a canvas, the passed image is uncompressed + all pre-multiplied.
When exported, its uncompressed or recompressed with a different algorithm, and un-multiplied.
All browsers and devices will have different rounding errors happening in this process
(see Canvas fingerprinting).
So if one wants a base64 version of an image file, they have to request it again (most of the time it will come from cache) but this time as a Blob.
Then you can use a FileReader to read it either as an ArrayBuffer, or as a dataURL.
function toDataURL(url, callback){
var xhr = new XMLHttpRequest();
xhr.open('get', url);
xhr.responseType = 'blob';
xhr.onload = function(){
var fr = new FileReader();
fr.onload = function(){
callback(this.result);
};
fr.readAsDataURL(xhr.response); // async call
};
xhr.send();
}
toDataURL(myImage.src, function(dataURL){
result.src = dataURL;
// now just to show that passing to a canvas doesn't hold the same results
var canvas = document.createElement('canvas');
canvas.width = myImage.naturalWidth;
canvas.height = myImage.naturalHeight;
canvas.getContext('2d').drawImage(myImage, 0,0);
console.log(canvas.toDataURL() === dataURL); // false - not same data
});
<img id="myImage" src="https://dl.dropboxusercontent.com/s/4e90e48s5vtmfbd/aaa.png" crossOrigin="anonymous">
<img id="result">
A more modern version of kaiido's answer using fetch would be:
function toObjectUrl(url) {
return fetch(url)
.then((response)=> {
return response.blob();
})
.then(blob=> {
return URL.createObjectURL(blob);
});
}
https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
Edit: As pointed out in the comments this will return an object url which points to a file in your local system instead of an actual DataURL so depending on your use case this might not be what you need.
You can look at the following answer to use fetch and an actual dataURL: https://stackoverflow.com/a/50463054/599602
shiv / shim / sham
If your image(s) are already loaded (or not), this "tool" may come in handy:
Object.defineProperty
(
HTMLImageElement.prototype,'toDataURL',
{enumerable:false,configurable:false,writable:false,value:function(m,q)
{
let c=document.createElement('canvas');
c.width=this.naturalWidth; c.height=this.naturalHeight;
c.getContext('2d').drawImage(this,0,0); return c.toDataURL(m,q);
}}
);
.. but why?
This has the advantage of using the "already loaded" image data, so no extra request is needed. Additionally it lets the end-user (programmer like you) decide the CORS and/or mime-type and quality -OR- you can leave out these arguments/parameters as described in the MDN specification here.
If you have this JS loaded (prior to when it's needed), then converting to dataURL is as simple as:
examples
HTML
<img src="/yo.jpg" onload="console.log(this.toDataURL('image/jpeg'))">
JS
console.log(document.getElementById("someImgID").toDataURL());
GPU fingerprinting
If you are concerned about the "preciseness" of the bits then you can alter this tool to suit your needs as provided by #Kaiido's answer.
its 2022, I prefer to use modern createImageBitmap() instead of onload event.
*note: image should be same origin or CORS enabled
async function imageToDataURL(imageUrl) {
let img = await fetch(imageUrl);
img = await img.blob();
let bitmap = await createImageBitmap(img);
let canvas = document.createElement("canvas");
let ctx = canvas.getContext("2d");
canvas.width = bitmap.width;
canvas.height = bitmap.height;
ctx.drawImage(bitmap, 0, 0, bitmap.width, bitmap.height);
return canvas.toDataURL("image/png");
// image compression?
// return canvas.toDataURL("image/png", 0.9);
};
(async() => {
let dataUrl = await imageToDataURL('https://en.wikipedia.org/static/images/project-logos/enwiki.png')
wikiImg.src = dataUrl;
console.log(dataUrl)
})();
<img id="wikiImg">
Use onload event to convert image after loading
function loaded(img) {
let c = document.createElement('canvas')
c.getContext('2d').drawImage(img, 0, 0)
msg.innerText= c.toDataURL();
}
pre { word-wrap: break-word; width: 500px; white-space: pre-wrap; }
<img onload="loaded(this)" src="https://cors-anywhere.herokuapp.com/http://lorempixel.com/200/140" crossorigin="anonymous"/>
<pre id="msg"></pre>
This is all you need to read.
https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsBinaryString
var height = 200;
var width = 200;
canvas.width = width;
canvas.height = height;
var ctx = canvas.getContext('2d');
ctx.strokeStyle = '#090';
ctx.beginPath();
ctx.arc(width/2, height/2, width/2 - width/10, 0, Math.PI*2);
ctx.stroke();
canvas.toBlob(function (blob) {
//consider blob is your file object
var reader = new FileReader();
reader.onload = function () {
console.log(reader.result);
}
reader.readAsBinaryString(blob);
});
In HTML5 better use this:
{
//...
canvas.width = img.naturalWidth; //img.width;
canvas.height = img.naturalHeight; //img.height;
//...
}