I want to perform a javascript xhr request for a png file from a C# webserver which I wrote.
Here is the code I use
var imgUrl = "http://localhost:8085/AnImage.png?" + now;
var request = new XMLHttpRequest();
request.open('GET', imgUrl, false);
request.send(); // this is in a try/catch
On the server-side I send back the file and add a Content-Disposition header.
I obtain the following response
I made sure that Content-Disposition was attached in the headers after the Content-Type (the screenshot is from Firebug, which appends in alphabetical order).
The results is that no dialog box is triggered, am I missing something in the response?
edit:
I want to perform everything in javascript for several reasons.
First: I don't want to show the image and I want to keep everything behind the curtain.
Second: when requesting the image I want the Content-Disposition to be added only on particular requests. Such requests are marked with a "Warning" header with value "AttachmentRequest"
request.setRequestHeader("Warning","AttachmentRequest");
I don't think Content-Disposition triggers any file save dialog when the request is via XHR. The use of XHR suggests you're going to handle the result in code.
If you want the user to be prompted to save the image to a file, I've used this technique successfully:
window.open("http://localhost:8085/AnImage.png?" + now);
It has the downside that it flashes a blank open window briefly until the header arrives, then the new window closes and the "save file" dialog box appears.
Using an iframe may prevent the window flashing:
var f = document.createElement('iframe');
f.style.position = "absolute";
f.style.left = "-10000px";
f.src = "http://localhost:8085/AnImage.png?" + now;
document.body.appendChild(f);
Separately, I wonder what effect (if any) Content-Disposition has on the handling of an img element:
var img = document.createElement('img');
img.style.position = "absolute";
img.style.left = "-10000px";
img.src = "http://localhost:8085/AnImage.png?" + now;
document.body.appendChild(img);
I haven't tried that, but the browser might respect the header. You'd need to be sure to test on all of the browsers you want to support.
Related
Task
I am currently trying to create a web extension for Firefox.
It should be able to read images' pixels.
For that purpose, I am rendering the image on an invsible canvas, and then I want to read it.
Example Code
function getImdata(reference) {
var canvas=document.createElement("canvas");
canvas.width=reference.naturalWidth;
canvas.height=reference.naturalHeight;
var context=canvas.getContext("2d");
context.drawImage(reference,0,0);
return context.getImageData(0,0,reference.naturalWidth,reference.naturalHeight); //Here I actually get the error...
}
Problem
However, I am getting a "Security Error" if I use "getImageData()".
Question
So I need a workaround, but couldn't find anything myself.
How can I read images' pixels without getImageData() ?
EDIT
Apparently it has something to do with CORS : HTML5 Canvas getImageData and Same Origin Policy
Thanks in advance!
There is. Since you're running from an extension your extension will have privileged access to cross-origin sources but only if loaded via fetch() and XMLHttpRequest() from a content script (or background script) - excerpt from that link:
Content scripts get the same cross-domain privileges as the rest of
the extension: so if the extension has requested cross-domain access
for a domain using the permissions key in manifest.json, then its
content scripts get access that domain as well.
This is accomplished by exposing more privileged XHR and fetch
instances in the content script [...]
Please note that these calls when called from a content script will not set origin and referer headers which sometimes can cause problems if the cross-origin site expects these to be set - for those cases you will need to use the non-privileged content.XMLHttpRequest or content.fetch() which will bring you back to square one.
The permissions in the manifest file (or if set permissions dynamically) must also allow access to these cross-origin sites.
This means however that you will have to "reload" the image source separately via these calls. You can do this the following way by first obtaining the original URL to the image you want to load, say, from a content script:
// example loading all images in current tab
let images = document.querySelectorAll("img");
for(let image of images) loadAsBitmap(image.src); // some sub-call using the url
Then load that source via the content script's fetch():
fetch(src).then(resp => { // load from original source
return resp.blob(); // obtain a blob
}).then(blob => { // convert blob, see below
// ...
};
When the blob is obtained you can convert it to an Object-URL and set that as source for an image and be able to go around the cross-origin restriction we otherwise face. In the content script, next steps would be:
let url = URL.createObjectURL(blob); // attach Object-URL to blob
let img = new Image(); // create image element *
img.onload = () => { // attach handler
let c = document.createElement("canvas"); // create canvas
let ctx = c.getContext("2d"); // get context
c.width = img.width; // canvas size = image
c.height = img.height;
ctx.drawImage(img, 0, 0); // draw in image
URL.revokeObjectURL(url); // remove reference.
let imageData =
ctx.getImageData(0,0,c.width,c.height); // get image data
// .. callback to a function that handles the image data
};
img.src = url; // start loading blob
I'm loading a motion jpeg from third-party site, which I can trust. I'm trying to getImageData() but the browser (Chrome 23.0) complains that:
Unable to get image data from canvas because the canvas has been tainted by
cross-origin data.
There are some similar questions on SO, but they are using local file and I'm using third party media. My script runs on a shared server and I don't own the remote server.
I tried img.crossOrigin = 'Anonymous' or img.crossOrigin = '' (see this post on the Chromium blog about CORS), but it didn't help. Any idea on how can I getImageData on a canvas with cross-origin data? Thanks!
You cannot reset the crossOrigin flag once it is tainted, but if you know before hand what the image is you can convert it to a data url, see Drawing an image from a data URL to a canvas
But no, you cannot and should not be using getImageData() from external sources that don't support CORS
While the question is very old the problem remains and there is little on the web to solve it. I came up with a solution I want to share:
You can use the image (or video) without the crossorigin attribute set first and test if you can get a HEAD request thru to the same resource via AJAX. If that fails, you cannot use the resource. if it succeeds you can add the attribute and re-set the source of the image/video with a timestamp attached which reloads it.
This workaround allows you to show your resource to the user and simply hide some functions if CORS is not supported.
HTML:
<img id="testImage" src="path/to/image.png?_t=1234">
JavaScript:
var target = $("#testImage")[0];
currentSrcUrl = target.src.split("_t=").join("_t=1"); // add a leading 1 to the ts
$.ajax({
url: currentSrcUrl,
type:'HEAD',
withCredentials: true
})
.done(function() {
// things worked out, we can add the CORS attribute and reset the source
target.crossOrigin = "anonymous";
target.src = currentSrcUrl;
console.warn("Download enabled - CORS Headers present or not required");
/* show make-image-out-of-canvas-functions here */
})
.fail(function() {
console.warn("Download disabled - CORS Headers missing");
/* ... or hide make-image-out-of-canvas-functions here */
});
Tested and working in IE10+11 and current Chrome 31, FF25, Safari 6 (Desktop).
In IE10 and FF you might encounter a problem if and only if you try to access http-files from a https-script. I don't know about a workaround for that yet.
UPDATE Jan 2014:
The required CORS headers for this should be as follows (Apache config syntax):
Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Headers "referer, range, accept-encoding, x-requested-with"
the x-header is required for the ajax request only. It's not used by all but by most browsers as far as I can tell
Also worth noting that the CORS will apply if you are working locally regardless of if the resource is in the same directory as the index.html file you are working with. For me this mean the CORS problems disappeared when I uploaded it to my server, since that has a domain.
You can use base64 of the image on canvas,
While converting into base64 you can use a proxy URL (https://cors-anywhere.herokuapp.com/) before your image path to avoid cross-origin issue
check full details here
https://stackoverflow.com/a/44199382/5172571
var getDataUri = function (targetUrl, callback) {
var xhr = new XMLHttpRequest();
xhr.onload = function () {
var reader = new FileReader();
reader.onloadend = function () {
callback(reader.result);
};
reader.readAsDataURL(xhr.response);
};
var proxyUrl = 'https://cors-anywhere.herokuapp.com/';
xhr.open('GET', proxyUrl + targetUrl);
xhr.responseType = 'blob';
xhr.send();
};
getDataUri(path, function (base64) {
// base64 availlable here
})
I'm loading a motion jpeg from third-party site, which I can trust. I'm trying to getImageData() but the browser (Chrome 23.0) complains that:
Unable to get image data from canvas because the canvas has been tainted by
cross-origin data.
There are some similar questions on SO, but they are using local file and I'm using third party media. My script runs on a shared server and I don't own the remote server.
I tried img.crossOrigin = 'Anonymous' or img.crossOrigin = '' (see this post on the Chromium blog about CORS), but it didn't help. Any idea on how can I getImageData on a canvas with cross-origin data? Thanks!
You cannot reset the crossOrigin flag once it is tainted, but if you know before hand what the image is you can convert it to a data url, see Drawing an image from a data URL to a canvas
But no, you cannot and should not be using getImageData() from external sources that don't support CORS
While the question is very old the problem remains and there is little on the web to solve it. I came up with a solution I want to share:
You can use the image (or video) without the crossorigin attribute set first and test if you can get a HEAD request thru to the same resource via AJAX. If that fails, you cannot use the resource. if it succeeds you can add the attribute and re-set the source of the image/video with a timestamp attached which reloads it.
This workaround allows you to show your resource to the user and simply hide some functions if CORS is not supported.
HTML:
<img id="testImage" src="path/to/image.png?_t=1234">
JavaScript:
var target = $("#testImage")[0];
currentSrcUrl = target.src.split("_t=").join("_t=1"); // add a leading 1 to the ts
$.ajax({
url: currentSrcUrl,
type:'HEAD',
withCredentials: true
})
.done(function() {
// things worked out, we can add the CORS attribute and reset the source
target.crossOrigin = "anonymous";
target.src = currentSrcUrl;
console.warn("Download enabled - CORS Headers present or not required");
/* show make-image-out-of-canvas-functions here */
})
.fail(function() {
console.warn("Download disabled - CORS Headers missing");
/* ... or hide make-image-out-of-canvas-functions here */
});
Tested and working in IE10+11 and current Chrome 31, FF25, Safari 6 (Desktop).
In IE10 and FF you might encounter a problem if and only if you try to access http-files from a https-script. I don't know about a workaround for that yet.
UPDATE Jan 2014:
The required CORS headers for this should be as follows (Apache config syntax):
Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Headers "referer, range, accept-encoding, x-requested-with"
the x-header is required for the ajax request only. It's not used by all but by most browsers as far as I can tell
Also worth noting that the CORS will apply if you are working locally regardless of if the resource is in the same directory as the index.html file you are working with. For me this mean the CORS problems disappeared when I uploaded it to my server, since that has a domain.
You can use base64 of the image on canvas,
While converting into base64 you can use a proxy URL (https://cors-anywhere.herokuapp.com/) before your image path to avoid cross-origin issue
check full details here
https://stackoverflow.com/a/44199382/5172571
var getDataUri = function (targetUrl, callback) {
var xhr = new XMLHttpRequest();
xhr.onload = function () {
var reader = new FileReader();
reader.onloadend = function () {
callback(reader.result);
};
reader.readAsDataURL(xhr.response);
};
var proxyUrl = 'https://cors-anywhere.herokuapp.com/';
xhr.open('GET', proxyUrl + targetUrl);
xhr.responseType = 'blob';
xhr.send();
};
getDataUri(path, function (base64) {
// base64 availlable here
})
It's a perfectly dumb question. But I just wanted to clear my doubt. When a image is loading we can check loading state using onload or oncomplete events. But I just wanted to know how much portion of the images is loaded using JavaScript. Is it really possible?
My question is, Can we get image size from URL? and Can we get how much portion of image is loaded in some interval?
If you load the images via an XMLHttpRequest object you can bind to the progress event and check the function argument "event" properties "loaded" and "total". As always, support between various browsers might be a surprise.
From the linked MDN article:
var req = new XMLHttpRequest();
req.addEventListener("progress", onProgress, false);
req.open();
function onProgress(ev) {
if (evt.lengthComputable) {
var percentComplete = evt.loaded / evt.total;
// ...
} else {
// Unable to compute progress information since the total size is unknown.
}
}
[Update] Per commenter #Bergi's question if you want to add the image to the DOM once it has fully downloaded you could add a new image element with:
The "src" attribute equal to the URL you fetched via XHR (and hope that the browser cache will prevent redundant download).
Or set the "src" attribute to a data URI of the Base64 encoded image content and not worry about the browser cache.
var img = new Image(); // or document.createElement('img');
img.src = ... // URL from the above XHR request, or
img.src = 'data:image/png;base64,' + base64(xhrContent);
document.body.appendChild(img);
You can achieve this with the html5 file api for modern browsers which allows to read the filename, filesize, mimetype, and a reference to the file handle - check this example
Heya, so I'm running into a weird bug/edgecase. Check out the following code:
var i = new Image();
i.src = 'http://ia.media-imdb.com/images/M/MV5BMTIxOTAxNTc4NF5BMl5BanBnXkFtZTcwOTg1NzQyMQ##._V1._SX97_SY140_.jpg';
console.log(i.width);
This works fine in chrome (as it's referring to a real image), but fails in FF. Thought it may have to do with the 'at' signs or the double extension (eg. '._V1._SX97_SY140_.jpg'), but don't really know.
Thanks for any help.
keep in mind, that the image is loaded asynchronously. You need to assign a event handler for the load event of the image and get the width there:
var i = new Image();
i.onload = function() {
console.log(this.width);
}
i.src = 'http://ia.media-imdb.com/images/M/MV5BMTIxOTAxNTc4NF5BMl5BanBnXkFtZTcwOTg1NzQyMQ##._V1._SX97_SY140_.jpg';
A 403 response header means Forbidden (wiki), that you are not allowed to access the resource.
imdb.com might be doing this to prevent hotlinking of their images in other sites.