【问题标题】:Download a large file or show error下载大文件或显示错误
【发布时间】:2016-11-25 05:46:05
【问题描述】:

在我们正在开发的网站上,我们有一个下载链接,必须提供给用户。但是,在获取 url 时,服务器可以在 JSON 中提供错误消息(然后将设置适当的标头和适当的 http 状态代码)或提供文件。

目前,我们正在使用 iframe 下载此文件,但这会阻止我们查看错误消息。虽然原则上可以这样做,但不能跨域完成,并且浏览器之间读取错误数据似乎不同(因为浏览器会将 json 解释为 html 并在其周围创建 html 标签)

我曾考虑使用 xmlhttprequest2 下载文件,并将其提供给用户,但是下载的文件可能很大,因此必须将其流式传输给用户。

因此,我正在寻找一种方法来下载文件或根据 http 状态代码读取错误消息。

API 设置

我可以根据自己的意愿更改 API,但是 API 被设计为公共 API,并且被设计为 REST API。这意味着,API 应该尽可能简单,并且使特定客户端代码工作的变通办法不应该对其他客户端造成任何混乱(因此 API 和客户端代码是解耦的)。

正在下载的文件在服务器上加密,只能通过 URL 中提供的信息解密。因此,分块传输很困难,因为提取一个块需要服务器解密整个文件。

【问题讨论】:

  • 您是否可以控制服务器响应?您能否将响应更改为错误代码和错误消息或成功代码和文件 url?如果是这样,您可以进行 ajax 调用,并在成功时重定向到 url。如果这不起作用,请说明您的设置会阻止这种情况。
  • MIME 的预期文件类型是什么?
  • @TinMonkey,是的,我愿意。但总的来说,任何错误都是例外,我宁愿提出一个请求,但我刚刚开始使用下面描述的 HEAD 方法来实现。它还涉及 REST API,因此这样的重定向不会真正适合设计,但带有 HEAD 的方法当然会:)。
  • @guest271314 application/octet-stream
  • 你能在出错时重定向吗?然后,您可以将错误重定向到包含哈希中错误的 url。然后该站点可以做一个简单的 window.parent.functionToDisplayError(error)

标签: javascript html iframe xmlhttprequest fileapi


【解决方案1】:

然后将设置适当的标头和适当的 http 状态代码

如果该陈述是正确的,您可以使用与 preflighted requests 相同的概念来处理跨站点请求。预检请求首先使用OPTIONS 方法向另一个域上的资源发送HTTP 请求,以确定实际请求是否可以安全发送。

在您的情况下,您可以手动发送 HEAD 请求,而不是自动发送 OPTIONS 请求。 HEAD 方法与GET 相同,只是服务器不在响应中返回消息体。响应HEAD 请求的HTTP 标头中包含的信息应与响应GET 请求而发送的信息相同。由于您只获取标题而不是正文,因此对于大文件甚至任何文件都没有问题,除了标题之外什么都不会下载。

然后,您可以读取这些标头甚至状态代码,并根据此手动预检请求的结果,决定是应该将文件流式传输给用户还是在遇到错误时获取错误消息.

在不了解您的项目的情况下使用状态码的基本实现可能如下:

function canDownloadFile(url, callback)
{
  var http = new XMLHttpRequest();
  http.open('HEAD', url);
  http.onreadystatechange = function() {
    if (http.readyState === XMLHttpRequest.DONE) {
      callback(http.status);
    }
  };

  http.send();
}

var canDownloadCallback = function (statusCode) {
  if (statusCode === 200) {
    // The HEAD request returned an OK status code, the GET will do the same,
    // let's download the file...
  } else {
    // The HEAD request returned something else, something is wrong,
    // let's fetch the error message and maybe display it...
  }
}

【讨论】:

  • 嘿,这正是我现在正在实施的!我希望有一些单一的请求替代方案,但如果没有,我会接受你的回答。
【解决方案2】:

您可以返回 iframe JS 代码,该代码将通过 postMessage 向父窗口发送消息,这样您可以捕获主机窗口中的错误。跨域不会有任何问题,并且内容可以像以前一样轻松地流式传输到浏览器。

【讨论】:

  • 这需要我将服务器响应与不一定是浏览器的客户端响应相匹配。
【解决方案3】:

您可以使用单个请求,以Blob 形式返回响应,检查Blob.type 以确定在何处使用FileReader .resultJSON 检索JSON 文本并显示错误消息,或设置@ 987654329@ 的<img> 元素使用URL.createObjectURL

var result = document.querySelector("div");

fetch("/path/to/resource")
.then(response => response.blob())
.then(blob => {
  if (blob.type === "application/json") {
    var reader = new FileReader();
    reader.onload = (e) => {
      result.innerHTML = JSON.parse(e.target.result).error
    };
    reader.readAsText(blob);
  } else {
    var img = document.createElement("img");
    img.src = URL.createObjectURL(blob);
    result.appendChild(img);
  }
});

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

【讨论】:

  • 这适用于大文件吗?这会将整个文件加载到内存中,对吧?
  • @user23127 “这对大文件有效吗?”你试过了吗?
  • 我很确定它不会。
  • @user23127 是的,可以使用 Answer 中的模式下载文件。在 plnkr 使用的方法模拟了 JSON 被随机返回并在 .then() 处处理。如果返回文件,您可以使用设置为<img> src 的相同objectURL 作为要下载的文件,使用带有download 属性集的<a> 元素,如果文件是响应,则只需要一个请求即可提供服务。请参阅版本 2 plnkr.co/edit/NLBe8XrgX4YxhLkAJL56?p=info ,其中当 JSON 未作为响应返回 Save File 时,将显示对话框;尝试多次运行 plnkr,直到文件作为响应返回。
  • 但是文件在保存之前仍然是下载的,所以它必须加载到内存中的某个地方。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-04-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-12
  • 1970-01-01
相关资源
最近更新 更多