3

I have a button on my page that, when clicked, will try to go out and download a file from a given URL on a different domain than my page's domain. Sometimes, that download URL will return an HTTP 429 response with an error message as content. I am trying to be able to handle both cases (successful download vs error).

Here is my code:

var w = window.open(url);
window.focus();
w.onload = function() {
  console.log(w.document.body.innerHTML);
};

The above code is run on a button click. What I'm seeing is that the w.onload function is never getting triggered. What is wrong with using this as a method of downloading a file?

Jim
  • 4,509
  • 16
  • 50
  • 80
  • _"What is wrong with using this as a method of downloading a file?"_ : _"will try to go out and download a file from a given URL on a different domain"_ – guest271314 Aug 09 '16 at 17:04
  • If url is a different domain, you are not going to have access to its DOM – Patrick Evans Aug 09 '16 at 17:06
  • Right, I see your comments and I agree. I have noted that this is not working - I am asking about alternatives. I need to either download the file or handle a 429 – Jim Aug 09 '16 at 17:13

2 Answers2

5

You can use XMLHttpRequest(), with error event attached to request resource. If request is not successful, error should be handled at error event handler of XMLHttpRequest().

You could alternatively use fetch() to achieve same result.

Set .responseType to "blob" at XMLHttpRequest or return response.blob() at fetch(). Create a new Blob with type set to "application/octet-stream", call window.open() with URL.createObjectURL() as parameter where Blob is parameter to .createObjectURL(), which should open Save File dialog.

You can chain .catch() to fetch() to handle errors.

<body>
  <a href="#" data-link="file.txt">download file.txt</a>
  <a href="#" data-link="http://stackoverflow.com">download stackoverflow.com</a>
  <script>

    var request = (path) =>
      fetch(path)
      .then(response => response.blob())
      .then(blob => window.open(
        URL.createObjectURL(
          new Blob([blob], {
            type: "application/octet-stream"
          })
        ), "_self"))
      // handle request error
      .catch((err) => {console.log(err); throw err});

    var a = document.querySelectorAll("a");
    for (let link of a) {
      link.onclick = (e) => {
        e.preventDefault();
        request(e.target.dataset.link)
        .catch((err) => {
          alert(err.message + "\ndownload of " + e.target.dataset.link + " error")
        })
      }
    }
  </script>
</body>

plnkr http://plnkr.co/edit/J40cPoLJ2WwESRzsSRhL?p=preview


Alternatively using <a> element, download attribute to suggest file name at Save File dialog

<body>
  <a href="#" data-link="file.txt">download file.txt</a>
  <a href="#" data-link="http://stackoverflow.com">download stackoverflow.com</a>
  <script>

    var request = (path) =>
      fetch(path)
      .then(response => response.blob())
      .then(blob => {
        var type = blob.type.split("/").pop();
        type = type === "plain" ? "txt" : type;
        var d = document.createElement("a");
        d.className = "download";
        d.download = "file-" + new Date().getTime() + "." + type;
        d.href = URL.createObjectURL(blob);
        document.body.appendChild(d);
        d.click();
        d.parentElement.removeChild(d);
      })
      // handle request error
      .catch((err) => {console.log(err); throw err});

    var a = document.querySelectorAll("a[href='#']");
    for (let link of a) {
      link.onclick = (e) => {
        console.log(e)
        e.preventDefault();
        request(e.target.dataset.link)
        .catch((err) => {
          alert(err.message + "\ndownload of " + e.target.dataset.link + " error")
        })
      }
    }
  </script>
</body>
guest271314
  • 1
  • 15
  • 104
  • 177
1

For handling the download of the file, the download attribute could be a solution (with polyfill).

But for the handling the HTTP errors in my opinion the best solution is using some back-end proxy which will check the HTTP headers and also will force the browser to download a specific file.

Community
  • 1
  • 1
Tomasz Dziuda
  • 346
  • 1
  • 4