Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to programmatically detect size limit for data url?

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

like image 839
Upgradingdave Avatar asked Apr 28 '16 14:04

Upgradingdave


People also ask

How big can a data URI be?

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.

What is the data size limit of hyperlink?

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).

How long can a URL be API?

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.


1 Answers

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.

like image 84
Kaiido Avatar answered Oct 06 '22 00:10

Kaiido