【问题标题】:Downloading a file (or handling an HTTP error) on click单击时下载文件(或处理 HTTP 错误)
【发布时间】:2016-12-15 20:19:45
【问题描述】:

我的页面上有一个按钮,当单击该按钮时,将尝试从与我的页面域不同域上的给定 URL 下载文件。有时,该下载 URL 将返回 HTTP 429 响应,其中包含错误消息作为内容。我试图能够处理这两种情况(成功下载与错误)。

这是我的代码:

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

以上代码是在单击按钮时运行的。我看到的是 w.onload 函数永远不会被触发。使用它作为下载文件的方法有什么问题?

【问题讨论】:

  • “使用这种方法下载文件有什么问题?” : “将尝试从给定的 URL 下载文件不同的域”
  • 如果 url 是不同的域,您将无法访问其 DOM
  • 好吧,我看到了你的 cmets,我同意。我注意到这不起作用 - 我在询问替代方案。我需要下载文件或处理 429

标签: javascript html


【解决方案1】:

您可以使用XMLHttpRequest(),将error 事件附加到请求资源。如果请求不成功,则应在XMLHttpRequest()error 事件处理程序处处理错误。

您也可以使用fetch() 来获得相同的结果。

XMLHttpRequest 处将.responseType 设置为"blob" 或在fetch() 处返回response.blob()。创建一个新的Blob,将type 设置为"application/octet-stream",调用window.open(),使用URL.createObjectURL() 作为参数,其中Blob.createObjectURL() 的参数,这应该会打开Save File 对话框。

您可以将.catch() 链接到fetch() 以处理错误。

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

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


或者使用&lt;a&gt; 元素,download 属性在Save File 对话框中建议文件名

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

【讨论】:

  • 这很有趣……很有意义。你如何保持文件类型呢?创建一个 blob,您将完全丢失该文件类型。假设它是一个 mp4 视频文件,你如何检测它并将其重建为正确的文件类型?
  • @Jim "你如何保持文件类型?创建一个 blob,你会完全失去那个文件类型。假设它是一个 mp4 视频文件,你如何检测并重建它是正确的文件类型吗?” 文件类型不会丢失。您可以使用&lt;a&gt; 元素、download 属性(如果在浏览器中可用)来设置文件名,包括最初返回的Blob 的扩展名response。尽管请注意,用户可以在Save File 对话框中更改建议的文件名或扩展名
【解决方案2】:

为了处理文件的下载,download 属性可能是一个解决方案(使用polyfill)。

但在我看来,为了处理 HTTP 错误,最好的解决方案是使用一些后端代理来检查 HTTP 标头以及 will force the browser to download a specific file

【讨论】:

    猜你喜欢
    • 2012-11-10
    • 2020-09-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-15
    • 1970-01-01
    • 2015-02-06
    • 2015-05-05
    相关资源
    最近更新 更多