Avi
Avi

Reputation: 391

Problems downloading big file(max 15 mb) on google chrome

I have a downloading problem in Google Chrome. I am using Ruby 2.2, Rails 4.2, AngularJS 1.2.

We dont have a database here. Everything we are getting through API. The file which we are trying to download is around 7 mb. It gives us "Failed: Network Error". Though it works fine on Firefox.

From the API we are getting binary data in JSON. We are parsing it. And then:

send_data response_fields["attachment"], type: response_fields["mimeType"], disposition: 'attachment', filename: params[:filename]

As we are using AngularJS, we are catching that value in AngularJS Controller and then converting it as:

var str = data;
var uri = "data:" + mimeType + ";base64," + str;

var downloadLink = document.createElement("a");
downloadLink.href = uri;
downloadLink.download = filename;
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);

This works in Firefox & even Chrome for smaller file size. Not sure why it is giving error for bigger size on Chrome.

Any suggestions?

Thanks.

Upvotes: 13

Views: 17817

Answers (2)

Dan Willett
Dan Willett

Reputation: 1000

This answer is more specific to the question: [Creating iframe with dataURI fails for large files, any workaround?] (Creating iframe with dataURI fails for large files, any workaround?)

However, that question was marked as a duplicate of this question ( it really isn't a duplicate ), the answers that were here previously did not directly address that question. It would have helped me if there was a good answer to that question. I was able to come up with a solution to that question, and I have reproduced it below.

Here is an example function that will take a dataURI, convert it to a blob and open it in an iframe.

function dataURIToiFrame(dataURI) {

        function dataURIToBlob(dataURI) {

            let binStr = window.atob(dataURI.split(',')[1]),
              len = binStr.length,
              arr = new Uint8Array(len),
              mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];

            for (let i = 0; i < len; i++) {
              arr[i] = binStr.charCodeAt(i);
            }

            return new Blob([arr], {
              type: mimeString
            });

          }

        let iframe = document.getElementById("iframe1");
        if (!iframe) {
            $("body").append(`
<a id="iframe1_wrapper" href="javascript:$('#iframe1_wrapper').remove();" style="position:absolute;right:0; top:50px; bottom:0; height:calc(100vh - 60px); width:650px; padding:20px;">
    <iframe id="iframe1" style="position: absolute;height:calc(100vh - 60px); width:650px; padding:0px;"></iframe><span style="position: relative; color: white;left: 10px">x</span>
</a>`);
            iframe = document.getElementById("iframe1");
        }

        iframe.src = URL.createObjectURL(dataURIToBlob(dataURI));       
}


Upvotes: 0

Kaiido
Kaiido

Reputation: 136986

This is an almost duplicate of these questions 1 and 2, but since they do deal particularly with the canvas element, I'll rewrite a more global solution here.

This problem is due to a size limit chrome has set in the anchor (<a>) download attribute. I'm not quite sure why they did it, but the solution is pretty easy.

Convert your dataURI to a Blob, then create an ObjectURL from this Blob, and pass this ObjectURL as the anchor's download attribute.

// edited from https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob#Polyfill
function dataURIToBlob(dataURI) {

  var binStr = atob(dataURI.split(',')[1]),
    len = binStr.length,
    arr = new Uint8Array(len),
    mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]

  for (var i = 0; i < len; i++) {
    arr[i] = binStr.charCodeAt(i);
  }

  return new Blob([arr], {
    type: mimeString
  });

}

var dataURI_DL = function() {

  var dataURI = this.result;
  var blob = dataURIToBlob(dataURI);
  var url = URL.createObjectURL(blob);
  var blobAnchor = document.getElementById('blob');
  var dataURIAnchor = document.getElementById('dataURI');
  blobAnchor.download = dataURIAnchor.download = 'yourFile.mp4';
  blobAnchor.href = url;
  dataURIAnchor.href = dataURI;
  stat_.textContent = '';

  blobAnchor.onclick = function() {
    requestAnimationFrame(function() {
      URL.revokeObjectURL(url);
    })
  };
};

// That may seem stupid, but for the sake of the example, we'll first convert a blob to a dataURI...
var start = function() {

  stat_.textContent = 'Please wait while loading...';
  var xhr = new XMLHttpRequest();
  xhr.responseType = 'blob';
  xhr.onload = function() {
    status.textContent = 'converting';
    var fr = new FileReader();
    fr.onload = dataURI_DL;
    fr.readAsDataURL(this.response);
  };
  xhr.open('GET', 'https://dl.dropboxusercontent.com/s/bch2j17v6ny4ako/movie720p.mp4?dl=0');
  xhr.send();

  confirm_btn.parentNode.removeChild(confirm_btn);
};

confirm_btn.onclick = start;
<button id="confirm_btn">Start the loading of this 45Mb video</button>
<span id="stat_"></span>
<br>
<a id="blob">blob</a>
<a id="dataURI">dataURI</a>

And a jsfiddle version for FF, since they don't allow the downloadattribute from stack-snippets...

Upvotes: 19

Related Questions