FileReader dragging image from browser window - javascript

I have a div where users can drag and drop an image and then, using FileReader, I get the base64 of the image. Works fine.
dropper.ondrop = function (e) {
e.preventDefault();
var file = e.dataTransfer.files[0];
var reader = new FileReader();
reader.readAsDataURL(file);
};
My issue is if I have a browser window open and drag and drop an image from the browser I get the error:
Failed to execute 'readAsDataURL' on 'FileReader':
parameter 1 is not of type 'Blob'.
Now, is there any turnaround to get the base64 of an image dragged from the browser window?
Ultimately, is there is a way to get the image URL and then the actual base64 with a server side curl request?
Any idea?

You can use e.dataTransfer.getData('url') to tell whether there's a URL available. Like this:
var url = e.dataTransfer.getData('url');
if(url){
console.log('Url');
console.log(url);
}else{
console.log('File');
var file = e.dataTransfer.files[0];
var reader = new FileReader();
reader.readAsDataURL(file);
}
Then, having the URL, you can load an img element and use draw it on a canvas to grab the base64 representation. Like this:
var img = document.createElement('img');
img.crossOrigin = 'anonymous';
img.onload = function(){
var canvas = document.createElement('canvas');
canvas.width = this.width;
canvas.height = this.height;
var context = canvas.getContext('2d');
context.drawImage(this, 0, 0);
console.log(canvas.toDataURL());
};
img.src = url;
This would be the "final" product:
var dropper = document.querySelector('div');
dropper.ondragover = function(e) {
e.preventDefault();
}
dropper.ondrop = function (e) {
e.preventDefault();
var url = e.dataTransfer.getData('url');
if(url){
console.log('Url');
console.log(url);
var img = document.createElement('img');
img.crossOrigin = 'anonymous';
img.onload = function(){
var canvas = document.createElement('canvas');
canvas.width = this.width;
canvas.height = this.height;
var context = canvas.getContext('2d');
context.drawImage(this, 0, 0);
console.log(canvas.toDataURL());
};
img.src = url;
}else{
console.log('File');
var file = e.dataTransfer.files[0];
var reader = new FileReader();
reader.readAsDataURL(file);
}
};
div{
border:1px solid red;
height:240px;
width:100%;
}
<div>Drop here</div>
Also available here: https://jsfiddle.net/8u6Lprrb/1/

The first parameter comes through with the "http://" or "https://" protocol instead of the expected "file://" protocol. and thats why you cant read it with the HTML5 FILE API, so you may want to download and save the file first before trying to read the contents.
on the other hand, you can update an img tag's src attribute with the retrieved url and skip the readDataAsUrl() portion to hotlink the image.

Related

Can't draw imported image through input file

I am trying to load an image with an html input type="file" and load it to an with javascript without success.
Here is my code so far:
HTML:
<canvas class="imported" id="imported"></canvas>
<button class="import_button" id="import_button">Import a picture</button>
<input type="file" id="imgLoader"/>
JAVASCRIPT:
document.getElementById('import_button').onclick = function() {
document.getElementById('imgLoader').click();
};
document.getElementById('imgLoader').addEventListener('change', importPicture, false);
function importPicture()
{
alert("HERE");
var canvas = document.querySelector('#imported');
var context = canvas.getContext("2d");
var fileinput = document.getElementById('imgLoader');
var img = new Image();
var file = document.getElementById('imgLoader').files[0];
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function(evt)
{
alert("THERE");
if( evt.target.readyState == FileReader.DONE)
{
alert("AGAIN");
img.src = evt.target.result;
context.drawImage(img, 200, 200);
}
}
}
The alerts are all fired up, no errors or message in the console..
How can I load it and display it? Thank you!
The logic (of your original code, not the edited version) is a bit baffling:
document.getElementById('imgLoader').addEventListener('change', importPicture, false);
adds a change event to the file input. That's fine. But then within the "importPicture" function, which runs when the file input changes, you add another change event listener (via fileinput.onchange) ...why are you doing that? I can't see how it makes sense. It means you have to change the file again before the code in there runs. And it also means that every time you change the file it adds more and more listeners, until you have lots of functions firing at once.
The other problem is that you're not waiting for the data to be loaded into the Image object before you try to draw the image on the canvas. You also need to set the dimensions of the canvas itself, and not be prescriptive about the dimensions of the image (because the user could upload anything, of any size).
Here's a working demo:
document.getElementById('import_button').onclick = function() {
document.getElementById('imgLoader').click();
};
var fileinput = document.getElementById('imgLoader').addEventListener('change', importPicture, false);
function importPicture() {
var canvas = document.querySelector('#imported');
var context = canvas.getContext("2d");
var img = new Image();
var file = this.files[0];
var reader = new FileReader();
reader.onload = function(evt) {
img.onload = function() {
canvas.width = img.width;
canvas.height = img.height;
context.drawImage(img, 0, 0);
}
img.src = evt.target.result;
}
reader.readAsDataURL(file);
}
<canvas class="imported" id="imported"></canvas>
<button class="import_button" id="import_button">Import a picture</button>
<input type="file" id="imgLoader" />
Credit to this answer for the final bits.

Input onchange javascript function call not working in Safari

I have an html canvas that shows a preview of an image when the image is selected in an input. This works in Chrome, but I can't seem to get it to work in Safari. Specifically - in Safari - the onchange="previewFile()" does not seem to call the previewFile function.
<canvas id="canvas" width="0" height="0"></canvas>
<h2>Upload a photo </h2>
<input type="file" onchange="previewFile()"><br>
<script type="text/javascript">
// setup the canvas
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
// grab the photo and display in canvas
var photo = new Image();
function previewFile() {
var file    = document.querySelector('input[type=file]').files[0];
var reader  = new FileReader(); 
reader.addEventListener("load", function () {
photo.src = reader.result;
canvas.height = photo.height;
canvas.width = photo.width;
ctx.drawImage(photo,0,0);
}, false);
if (file) {
  reader.readAsDataURL(file);
}
}
</script>
Your problem is certainly due to the fact that you are not waiting for the image has loaded before you try to draw it on the canvas.
Even wen the source is a dataURI, the loading of the image is asynchronous, so you need to wrap your drawing operations in the image's onload event.
var photo = new Image();
photo.onload = function(){
canvas.height = photo.height;
...
}
...
reader.onload = function(){
// this will eventually trigger the image's onload event
photo.src = this.result;
}
But if all you need is to draw your image on the canvas, don't even use a FileReader, actually, the readAsDataURL() method should trigger a I'm doing something wrong error in your brain. Almost all you can do with a dataURI version of a Blob, you can also do it with the Blob itself, without computing it nor polluting the browser's memory.
For example, to display an image for user input, you can use URL.createObjectURL(blob) method.
// setup the canvas
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
// grab the photo and display in canvas
var photo = new Image();
// drawing operations should be in the mage's load event
photo.onload = function() {
// if you don't need to display this image elsewhere
URL.revokeObjectURL(this.src);
canvas.height = this.height;
canvas.width = this.width;
ctx.drawImage(this, 0, 0);
}
photo.onerror = function(e) {
console.warn('file format not recognised as a supported image');
}
file_input.onchange = function() {
// prefer 'this' over DOM selection
var file = this.files[0];
var url = URL.createObjectURL(file);
photo.src = url;
};
<canvas id="canvas" width="0" height="0"></canvas>
<h2>Upload a photo </h2>
<input type="file" id="file_input">
<br>
And for those who need to send this file to their server, use a FormData to send directly the Blob. If you really need a dataURI version, then convert it server-side.

Image scraping and sending to the server

I scraping some pages (I know, I know, I shouldn't, but the info from our intranet is not available in any other reliable way). So I inject a small $(...).each(... $.ajax({})); JavaScript and this works fine.
I got most info out of it, but now I need the images. I can get the URL, but I need to store them on the server (or on my local machine first). I can't use the URL because the images are behind username/password authentication.
Can I send them with a $.ajax(multipart/form post - new FormData()) construct?
All idea's welcome.
One idea is to encode them as base64 strings and store them that way. Here's a fiddle demonstrating the method: http://jsfiddle.net/c8n9d4ce/
I also found this fiddle that likewise demonstrates this method.
My code from the fiddle:
var canvas = document.getElementById("myCanvas");
var srcImg = document.getElementById("sourceImage");
var final = document.getElementById("base64Image");
var img = new Image();
img.crossOrigin = 'Anonymous';
img.onload = function(){
canvas.height = this.height;
canvas.width = this.width;
canvas.getContext('2d').drawImage(this,0,0);
var dataURL = canvas.toDataURL();
canvas = null;
final.src = dataURL;
};
img.src = srcImg.src;
img {
max-width: 100%;
}
<h1>Original Image</h1>
<img src="https://i.imgur.com/5YJ3HQ9.jpg" id="sourceImage">
<h1>Canvas Rendered Image</h1>
<canvas id="myCanvas"></canvas>
<h1>Base64 Encoded Image</h1>
<img id="base64Image">
Seems the way to go, I tried, but got this error: "Cross-origin image load denied by Cross-Origin Resource Sharing policy". I tried the snippet on this webpage (just copy/paste into the console).
function getDataURL(srcImg, done) {
var img = new Image();
img.crossOrigin = 'Anonymous';
img.onload = function(){
var canvas = document.createElement('canvas');
canvas.height = this.height;
canvas.width = this.width;
canvas.getContext('2d').drawImage(this,0,0);
done(canvas.toDataURL());
};
img.src = srcImg.src;
}
getDataURL($("img")[0], function(data) {
console.log(data);
});

Upload Picture as base64 String to Parse

I have following Problem.
I´m using a canvas to resize pictures a user uploaded to my webapp.
Then I retrieve the picture from the canvas as a base64 encoded string and try to upload it to the parse server.
Since parse supports base64 I thought it could work.
When i upload the images however, there is no image but instead this message for every image file i upload:
{"_ContentType":"image/jpg","_ApplicationId":"MY APPLICATION
ID","_JavaScriptKey":"MY JAVASCRIPT
KEY","_ClientVersion":"js1.3.5","_InstallationId":"THE INSTALLATION
ID","_SessionToken":"THE SESSION TOKEN OF THE CURRENT USER"}
I can retrieve the files, but what i get is no image.
Here is my code for uploading the pictures:
var fileUploadControl = $("#product_pictureUploadModal")[0];
if (fileUploadControl.files.length > 0) {
//THE USER SUCCESSFULLY SELECTED A FILE
var file = fileUploadControl.files[0];
if(file.type.match(/image.*/)){
//RESIZE THE IMAGE AND RETURN a base64 STRING
var resizedImage = resizeImage(file);
//CREATE A PARSE FILE
var name = "picture.jpg";
var parseFile = new Parse.File(name, {base64: resizedImage}, "image/jpg");
parseFile.save().then(function() {
//IMAGE SUCCESSFULLY UPLOADED
},
function(error){
alert(error.message);
});
}else{
//THE USER DIDNT CHOSE A PICTURE
}
}else{
//THE USER DIDNT SELECT A FILE
}
Here is my code for resizing the pictures:
function resizeImage(file){
var MAX_WIDTH = 400;
var image = new Image();
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
var url = window.URL || window.webkitURL;
var src = url.createObjectURL(file);
image.src = src;
return image.onload = function(){
ctx.clearRect(0, 0, canvas.width, canvas.height);
if(image.width > MAX_WIDTH){
if(image.width > image.height){
image.height = (image.height%image.width)*400;
}else{
image.height = (image.height/image.width)*400;
}
alert("image height " + image.height);
image.width = 400;
}
canvas.width = image.width;
canvas.height = image.height;
ctx.drawImage(image, 0, 0, image.width, image.height);
url.revokeObjectURL(src);
var fileRezized = encodeURIComponent(canvas.toDataURL("image/jpg"));
return fileRezized;
};
}
What am i missing here?
I hope you guys could help me. I put a lot of work and time in this already and I seem to not get any further than this.
I want to resize the image in the browser because otherwise upload speed might be too slow for some people, as my application will be available for mobile phones as well.
greetins from germany, marvin
When you get no dataURL back from the browser, it's often caused by a cross-domain security violation.
The users image likely does not originate on the same domain as your web page, so when you drawImage the users image to the canvas, you are "tainting" the canvas. This tainting will cause .toDataURL to be disallowed which will in turn cause the DataURL uploaded to your server to be empty.
One workaround
As a workaround, you can "round-trip" the user's image:
upload the users image to your server and save it in the same domain as your web pages.
reload the users browser page along with their image served from your own domain.
Another workaround: FileReader
If your users have modern browsers that support FileReader then you can let your users upload images from their local drives in a security compliant way.
Here's example code using FileReader:
function handleFiles(files) {
for (var i = 0; i < files.length; i++) {
var file = files[i];
var imageType = /image.*/;
if (!file.type.match(imageType)) {
continue;
}
var img = document.createElement("img");
img.classList.add("obj");
img.file = file;
var reader=new FileReader();
reader.onload=(function(aImg){
return function(e) {
aImg.onload=function(){
var canvas=document.createElement("canvas");
var ctx=canvas.getContext("2d");
canvas.width=aImg.width;
canvas.height=aImg.height;
ctx.drawImage(aImg,0,0);
document.body.appendChild(document.createElement('br'));
document.body.appendChild(canvas);
var u=canvas.toDataURL();
var textarea=document.createElement('textarea');
textarea.value=u;
document.body.appendChild(textarea);
}
// e.target.result is a dataURL for the image
aImg.src = e.target.result;
};
})(img);
reader.readAsDataURL(file);
} // end for
} // end handleFiles
//
$("#fileElem").change(function(e){ handleFiles(this.files); });
body{ background-color: ivory; }
canvas{border:1px solid red;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<h4>Example: load images to canvas while satisfying security requirements.</h4>
<input type="file" id="fileElem" multiple accept="image/*">
<br>
There is an error in this line:
var fileRezized = encodeURIComponent(canvas.toDataURL("image/jpg"));
There is no mime-type image/jpg in canvas. This would actually default to image/png instead which I guess could confuse the receiver. Just correct the line to:
var fileRezized = encodeURIComponent(canvas.toDataURL("image/jpeg"));
I don't know parse.com, but also try doing this with this line:
var parseFile = new Parse.File(name, {base64: resizedImage}, "image/jpeg");
// ^^
Test case
var canvas = document.querySelector("canvas"), out = document.querySelector("div");
out.innerHTML = canvas.toDataURL("image/jpg") + "<br><br>"; // PNG
out.innerHTML += canvas.toDataURL("image/jpeg"); // JPEG
<canvas width=1 height=1></canvas><div></div>

Adding Image to a PDF using jspdf makes the image black

When i try to add the image in the URL to a PDF file the image comes completley black.
But when I click the download pdf button again the image gets added to the PDF.Only
when I do it the first time, the image comes as black.
function getBase64Image(url) {
alert(url);
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var img = new Image();
img.src = url;
img.style.height ="181px";
img.style.width ="183px";
//img.crossOrigin ="Anonymous";
context.drawImage(img,0,0);
var dataURL = canvas.toDataURL("image/jpeg");
alert(dataURL);
document.body.appendChild(img);
var doc = new jsPDF('landscape');
doc.addImage(img,'JPEG',0,0,50,50);
doc.save('Saved.pdf');
}
getBase64Image("http://localhost:64931/jspdf/download.png");
What happens when you change your code like this:
A changed JPEG to PNG, that worked for me.
function getBase64Image(url) {
alert(url);
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var img = new Image();
img.src = url;
img.style.height ="181px";
img.style.width ="183px";
//img.crossOrigin ="Anonymous";
context.drawImage(img,0,0);
var dataURL = canvas.toDataURL("image/png");
alert(dataURL);
document.body.appendChild(img);
var doc = new jsPDF('landscape');
doc.addImage(img,'PNG',0,0,50,50);
doc.save('Saved.pdf');
}
getBase64Image("http://localhost:64931/jspdf/download.png");
I was facing the same issue yesterday, and writing this answer incase someone finds it useful
What I was trying to achieve?
I was converting the doc (pdf) into a blob and then opening it in a print dialogue, but the image was black.
var blob = new Blob([doc.output()], { type: "application/pdf" });
This is how I was trying to convert it into a blob.
but this resulted in a black image
Solution
jsPDF allows us to add arguments in the output function.
[](jsPDF output api)
adding type as bloburl works for me, as i was opening a print dialogue using the blob url
let iframe = document.createElement("iframe"); //load content in an iframe to print later
document.body.appendChild(iframe);
//* print dialogue
iframe.style.display = "none";
iframe.src = blobURL;
console.log(blobURL) // same blobURL that was received from jsPDF
iframe.onload = function () {
setTimeout(function () {
iframe.focus();
iframe.contentWindow.print(); // opens a print dialogue box
}, 1);
};

Categories