I'm using javascript and html canvas to resize jpeg images. After resizing, I use canvas.toDataURL
as the href attribute in an anchor tag in order to provide a link where users can download the resized images.
This works nicely up to a certain image size.
It seems that different browsers have different limits on the size of data urls as mentioned here: https://developer.mozilla.org/en-US/docs/Web/HTTP/data_URIs
In chrome, when I'm over the data url size limit, nothing happens when I click on the download link; no errors or anything (as far as I can tell).
Is there a some way to programmatically detect whether a data url is too large? Maybe some browser event that will show me whether clicking a download link failed?
Ideally, I'd like to detect whether the download was successful. When data urls are too large, I'd like to display an explanation to the end user describing how to right click on the image and choose "save as ...", which always seems to work.
Update 1:
It looks like using canvas.toBlob
is the best workaround for now. Here's the api documentation: https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob.
Here's a jsfiddle that demonstrates how anchor href download links fail when using toDataURL
for larger canvases, but toBlob
seems to work:
https://jsfiddle.net/upgradingdave/c76q34ac/3/
Here are some related stackoverflow questions: canvas.toDataURL() download size limit
Data protocol URL size limitations
Max URI length is 32KB. 2 Support is limited to images and linked resources like CSS or JS, not HTML files. Maximum size limit is 4GB.
Data URI Limits Chrome - 2MB for the current document. Otherwise the limit is the in-memory storage limit for arbitrary blobs: if x64 and NOT ChromeOS or Android, then 2GB; otherwise, total_physical_memory / 5 (source).
URL length The REST API supports Uniform Resource Locators (URLs) with a length of up to 6000 characters. To avoid exceeding this limit, it is important to be aware of URL encoding.
No, there doesn't seem to be any event letting you know if an anchor with the download
attribute actually succeeded to download the resource.
But, the limitation you are facing seems to only concern this situation : an anchor element <a>
with the download
attribute.
The browser can handle way longer dataURI (I think that in most browsers the limitation is the same as the one for strings length). E.g, it can load it to <img>
element, and more importantly in your case, it can process the dataURI string.
This is important because it allows you to convert this dataURI to a blob, then to create an object URL
from this blob. object URL
's URI are small and don't face this length limitation in anchors with download
attribute.
So the best in your case is probably to directly use the canvas.toBlob
method (a polyfill is available on mdn), and to pass the object URL
's URI as the href
of your anchor.
var img = new Image();
img.crossOrigin = "anonymous";
img.onload = function(){
var ctx = document.createElement('canvas').getContext('2d');
ctx.canvas.width = this.width*10;
ctx.canvas.height = this.height*10;
ctx.drawImage(this,0,0,ctx.canvas.width, ctx.canvas.height);
// convert our canvas to a png blob
// (for type use the second parameter, third is for quality if type is image/jpeg or image/webp)
ctx.canvas.toBlob(function(blob){
myAnchor.href = URL.createObjectURL(blob);
});
// since we are dealing with use files, we absolutely need to revoke the ObjectURL as soon as possible
var revokeURL = function(){
// but we have to wait that the browser actually prepared the file to download
requestAnimationFrame(function(){
// we've waited one frame, it's seems to be enough
URL.revokeObjectURL(this.href);
this.href=null;
});
this.removeEventListener('click', revokeURL);
};
myAnchor.addEventListener('click', revokeURL);
};
img.src = 'http://lorempixel.com/200/200/';
<a id="myAnchor" download="anAwesomeImage.png">download</a>
[ Live Demo ] (since download
attribute is blocked by some UA in SE-Snippets)
But note that even if the string representation (URI) of the object URL
is short, it will actually take the size of your file in browser's memory, and so until you hard-refresh the page (clearing cache), or close the tab where you created this object URL. So you'll absolutely need to call URL.revokeObjectURL()
to clear this space.
But, since there is no event to know if the downloading actually succeeded, you're stuck with the onclick
event, which will fire before the file downloading occurred. From my tests, waiting for a single frame with requestAnimationFrame
is enough, but I may be wrong.
And for the ones coming here with an other source than a canvas for their dataURI, there are already a lot of posts in SO about converting a dataURI to a blob, and you can just check the mdn polyfill provided above, they're doing it too.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With