I've run into an error that a lot of people seem to have gotten in the past but the explanations are not clear to me. After using the .getImageData() method in my javascript to get the pixel data of a picture that I have stored on my computer and inputed into an img tag in my HTML webpage I get the SecurityError: DOM Exception 18.
I'm simply running an HTMl file in my browser and it is preventing me from getting this stuff done. I've read about making a proxy to get around it or something or hosting the image on my own domain. I don't exactly understand what this means or how to go about doing it. So, it'd be greatly appreciated if anyone could help. I'll input my code here and leave a link to the image as well.
http://i.imgur.com/8UzWlWX.png
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Test</title>
<script>
function putimg() {
var img = document.getElementById('img');
var can = document.getElementById('C');
var ctx = can.getContext('2d');
ctx.drawImage(img, 0, 0);
var position = [];
var imgdata = ctx.getImageData(0,0, img.width, img.height);
var data = imgdata.data;
for (var i = 0; i < data.length; i += 4) {
if (data[i] == 255) {
var vertical = Math.floor((data.indexOf(data[i]) / 4) / 400);
var horizontal = Math.floor((data.indexOf(data[i]) / 4) % 400);
position.push(horizontal, vertical);
}
}
}
ctx.putImageData(imgdata, 0, 0);
}
</script>
</head>
<body onload="putimg()">
<h1>Test</h1>
<img id="img" src="circle-image-alone.png" style="display:none"></img>
<canvas id="C" width="600" height="600"></canvas>
</body>
</html>
EDIT: the file is now in the same folder as the html file, but the same error flag appears
The problem is that this is breaking Same-origin policy. You need to either have imgur relax their Cross-origin sharing or host the image yourself.
If you are running the HTML file directly from the file system on your computer then you can just download the image to the same folder as your html file is in, and reference it like
<img id="img" src="8UzWlWX.png" style="display:none"></img>
Here are the hoops you need to jump through to make it work through a foreign origin like imgur: https://developer.mozilla.org/en-US/docs/HTML/CORS_Enabled_Image
As you've discovered, your browser considers even your local file system to be a different domain.
So you’re still violating cross-domain even if your image and .html are in the same local directory.
You need a CORS compliant way of loading your image.
Here’s how:
Open up an account on dropbox.com and host your image there.
Be sure to put your image in the “public” folder they set up for you.
They allow cross-domain downloading of their images from your “public” folder.
To get the URL of your dropbox image:
Right-click the image in your public folder and select “copy public link”.
This will put the url into your clipboard where you can paste it into your code.
Then use this code to download your image into the browser in a CORS compliant way:
// download an image from dropbox.com
// in a CORS compliant way
var img=new Image();
img.onload=function(){
ctx.drawImage(img, 0, 0);
}
img.crossOrigin="anonymous";
img.src="dropbox.com/yourDropboxAccount/yourImage.png";
The result is an image that you can fully examine/change/save with:
ctx.getImageData
ctx.putImageData
ctx.toDataURL.
Related
I have spent my entire day trying to delete EXIF data from images with React. I want to do this for try to solve another problem, when user uploads an image with XMP data, apparently AWS WAF blocks it (I didn't have problems with another kind of data, I dind't tried everything tho, but I used an image with camera information and blabla and it worked just fine. Plus, I deleted XMP data from problematic images with a software, and WAF didn't blocked it anymore). So, I want to delete XMP data (author, creator tool, etc.) before send it to server.
I'm using react-dropzone for upload the images, and today I tried different libraries for achieve this, without success. With exifr and exif-js I can see all the EXIF information (including XMP) and specificcaly the data that is causing the error in the upload, but I didn't find a way to remove that from the picture. On the other hand, I used piexifjs and worked fine for delete information, but I couldn't find XMP data!! Just gps, EXIF with camera and picture settings, plus a couple of thing not of my interest. What can I do? I'm able to see this information with exif-js and exifr, but I just can delete things with pixiefjs in which I can't see xmp data.
I do not provide code because it is not an error as such, I want library recommendations or, if exists, a way to configure piexifjs for find this kind of data. exif-js and exifr are for reading, not for removing.
Thanks a lot.
Here would be a sample code of mine:
With a right on the image you should be able to download the image and there should be no exif data included. But I am unsure if there is a better way to do that.
document.getElementById('upload').onchange = function() {
const img = new Image();
img.src = URL.createObjectURL(this.files[0]);
img.onload = function() {
//canvas
const canvas = document.createElement('canvas');
canvas.width = this.width;
canvas.height = this.height;
const ctx = canvas.getContext('2d');
ctx.drawImage(this, 0,0);
//image in html
newImg = document.getElementById('img');
newImg.src = canvas.toDataURL();
newImg.style.width = this.width;
newImg.style.height = this.height;
};
img.onerror = function() {console.error("The file is not an image")};
};
<!doctype html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<input type="file" id="upload">
<img id="img"></img>
<script src="script.js"></script>
</body>
</html>
I'm unsure if it couldn't be simpler, but you could create a canvas with the size of the image, load the image into it, and write the image data from the canvas to a new image file.
My issue only arises when the loaded image was preloaded somewhere else. For example, when I somewhere use a <img>-tag with the src attribute.
Have a look at this code:
<canvas id="myCanvas" width="300" height="150" style="border:1px solid #d3d3d3;" ></canvas>
<img src="https://local-spaces.fra1.digitaloceanspaces.com/test.jpg" width="50"/>
<button onclick="show()">Load Canvas Picture</button>
<script>
function show() {
const c = document.getElementById("myCanvas");
const ctx = c.getContext("2d");
const img = new Image();
img.setAttribute('crossOrigin', 'anonymous');
img.onload = function(){
ctx.drawImage(img,0,0, 300, 150);
};
img.src = "https://local-spaces.fra1.digitaloceanspaces.com/test.jpg"
}
</script>
Note: If you are seeing the image in canvas correctly, please cache+hardreload your browser to see the error.
If you are clicking on the button and open your console, you will see that you are getting a CORS-error:
Access to image at
'https://local-spaces.fra1.digitaloceanspaces.com/test.jpg' from
origin 'null' has been blocked by CORS policy: No
'Access-Control-Allow-Origin' header is present on the requested
resource.
Now let's take the next example to see that it is working without preloading the image: https://jsfiddle.net/akzxp9vs/
Note: To make this example work, it's super important that you are deleting your cache + hard reload your browser.
Only then you see that the correct header response is giving back.
Any ideas what I can do?
The image is on the S3 Cloud of Digital Ocean, called Spaces. The image itself is set to public and the CORS setting are set to:
The browser needs to know to check for CORS permissions when the HTTP request is made (i.e. to include an Origin header etc).
When you create a new Image object, it uses the cached data from the <img> element.
Add a crossorigin attribute to the existing <img> element or remove that <img> element entirely.
I found a better solution to my problem. #Quentin's solution is principally right, but it's just not practicable. I tried to update all <img>-tags, however in my page I have dozens of packages that are using the <img>-tag as well.
For example Leaflet. It's like Google Maps, but with Open Street Maps. You just don't have the control over to set attributes like crossorigin in custom icons.
So there had to be another solution. It feels bad to use it, but it does the job.
const src = new URL(imgUrl);
src.searchParams.append('cors', Date.now());
return src.href;
This code appends to your URL a query parameter and forces Chrome to reload the image without being cached.
Here is the html element we are trying to attach a file to:
<input id="uploaded_file_file_for_upload" class="file optional" type="file" name="uploaded_file[file_for_upload]">
User already selects a file for the element above. What we need to do is to remove the current file selection and attach a new file to it. The new file is an image file and is saved in a canvas element. Here is the js code for new file:
function resize(image, width, height) {
var mainCanvas = document.createElement("canvas");
mainCanvas.width = width;
mainCanvas.height = height;
var ctx = mainCanvas.getContext("2d");
alert(width + ' resize to ' + height);
ctx.drawImage(image, 0, 0, width, height);
.....(remove current and attach the new file to the elelment#uploaded_file_file_for_upload)
};
We tried the following to attach and it did not work:
$('#uploaded_file_file_for_upload').attr('src', mainCanvas.toDataURL("image/jpeg"));
The problem may be that it is not an image element. What's the right way to remove and attach a file type?
The short answer is you can't. The only way to upload a file is to ship the file selected by the user, with no change.
The only way to do exactly what you are trying to do here is using :
a native app
a hybrid app with native plugin
via a browser plugin (flash, chrome app...)
Unless, the browser security policy will block you.
Two workarounds could be:
send the original file and resize it server side (php library, nodejs...)
or resize the image as you want, then uploading the base64 encoded image data of the edited image via a POST ajax request to your server, and then write it to a file server side.
I have download the source from this http://www.storminthecastle.com/projects/imagefilters1/. It is regarding some image manipulation in html5 canvas.
Inside the source, it will load the image located in a local directory...
function reset() {
imageURL = "./sandbox.jpg";
imageFilter = grayscale;
document.querySelector("#filename").innerHTML = "";
update();
}
The above is working in my project. But I am trying to load an image from an url, so I modified it to the following...
function reset() {
imageURL = "http://xxxxxx.jpg";
imageFilter = grayscale;
document.querySelector("#filename").innerHTML = "";
update();
}
When I test it, the image is being displayed correctly. However, all the features are not working anymore and I don't know why. I have no idea why it cannot take in url as the argument and I don't know how to modify it to make it work. Any helps?
Thanks for the link provided. I further read on the COR issue and managed to locate that line of coding to be added.
img.crossOrigin = '';
//img domain different from app domain
img.src = 'http://xxx.jpg';
Simply set the crossOrigin property of the image to make it work. Basically, this will allow cross domain image for manipulation. Without it, any cross domain will be blocked and you will get the security exception. Really thanks for the helps! :)
To add-on, I have only tested using Chrome and is working.
I've web app running. And it uses ajax upload. The issue is recently users are uploading too big images. So it's taking a bit more time. Users are complaining about that. So what I was thinking is this, 'If I somehow crop and resize the image via js and then send it to the server via ajax upload then the time will be reduced'. So is there any way to do that? Any idea for it?
A solution is to use modern ways like FileReader and Canvas (But this works only on the latest modern browsers).
http://caniuse.com/filereader
http://caniuse.com/canvas
In this example i show how to let the client resize an image before uploading by setting a max width & height mantaining aspect ratio.
In this example max widthHeight = 64;
your final image is c.toDataURL();
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script>
var h=function(e){
var fr=new FileReader();
fr.onload=function(e){
var img=new Image();
img.onload=function(){
var MAXWidthHeight=64;
var r=MAXWidthHeight/Math.max(this.width,this.height),
w=Math.round(this.width*r),
h=Math.round(this.height*r),
c=document.createElement("canvas");
c.width=w;c.height=h;
c.getContext("2d").drawImage(this,0,0,w,h);
this.src=c.toDataURL();
document.body.appendChild(this);
}
img.src=e.target.result;
}
fr.readAsDataURL(e.target.files[0]);
}
window.onload=function(){
document.getElementById('f').addEventListener('change',h,false);
}
</script>
</head>
<body>
<input type="file" id="f">
</body>
</html>
In the canvas part of the code you can also add a cropping function.
Edit as asked in the comments
c.toDataURL();
is the image base64_string you can store it in a hidden input,append to a new FormData() or wherever you want.
on the server
$data=explode(',',$base64_string);
$image=base64_decode($data[1]);
write to file
$f=fopen($fileName,"wb");
fwrite($f,$image);
fclose($f);
or
$gd=imagecreatefromstring($image);
you can also store the whole base64 image string in the database and always use that.