【问题标题】:Ajax - Get size of file before downloadingAjax - 下载前获取文件大小
【发布时间】:2013-06-29 06:30:05
【问题描述】:

基本上,我想根据文件大小确定是否应该使用 AJAX 下载文件。

我想这个问题也可以改写为:如何只获取 ajax 请求的标头?


编辑:cmets 中的ultima-rat0 告诉我已经提出的两个问题显然与这个问题相同。它们非常相似,但它们都想要 jQuery。我想要一个非 jQuery 解决方案。

【问题讨论】:

标签: javascript ajax http-headers filesize


【解决方案1】:

您可以手动获取XHR响应头数据:

http://www.w3.org/TR/XMLHttpRequest/#the-getresponseheader()-method

此函数将获取请求的 URL 的文件大小:

function get_filesize(url, callback) {
    var xhr = new XMLHttpRequest();
    xhr.open("HEAD", url, true); // Notice "HEAD" instead of "GET",
                                 //  to get only the header
    xhr.onreadystatechange = function() {
        if (this.readyState == this.DONE) {
            callback(parseInt(xhr.getResponseHeader("Content-Length")));
        }
    };
    xhr.send();
}

get_filesize("http://example.com/foo.exe", function(size) {
    alert("The size of foo.exe is: " + size + " bytes.");
});

【讨论】:

  • 如果我使用变量而不是警报?为什么他们返回“未定义”?我如何将xhr.getResponseHeader("Content-Length") 存储在变量中?
  • @FabioCalvosa xhr.open("HEAD", url, true); // false 使请求异步所以当你像这样调用该函数时: var myVar; get_filesize('sample.com');警报(myVar); // 你的变量是'未定义' 但是如果你像这样改变你的代码: var myVar; get_filesize('sample.com', function(){ alert(myVar); // 你的变量不会是'undefined' 因为你调用了call back 方法 });
  • 如果浏览器和服务器进行 gzip 编码,这将永远无法工作...禁止内容长度标头。更糟糕的是 W3C/xhr 禁止设置 Accept-Encoding 从而消除了客户端解决方法的任何希望......非常令人沮丧
  • 我在 Chrome 控制台中调用它,但它得到了 NaN。
  • 使用 HEADERS_RECEIVED 时标头可用时的 readyState 可能会更快
【解决方案2】:

有时 HEAD 的行为与 GET 不同,所以我建议这样的事情,在获得 Content-Length 标头后中止请求:

new Promise(resolve => {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', '/a.bin', true);
    xhr.onreadystatechange = () => {
        resolve(+xhr.getResponseHeader("Content-Length"));
        xhr.abort();
    };
    xhr.send();
}).then(console.log);

【讨论】:

  • 如果Content-Length 小于响应正文中实际收到的值怎么办?有没有办法从服务器获取未压缩的响应大小?
【解决方案3】:

如果 HEAD 请求不可行:

解决方案 Ebrahim 对我来说仅在 Firefox 中不起作用,因为上下文长度不适用于 Firefox 中的中止请求。所以我使用了 'onprogress' 事件而不是 'onreadystatechange' 事件:

new Promise(
  (resolve, reject) => {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', url, true);
    xhr.onprogress = (event) => {
      if (event.lengthComputable) {
        resolve(event.total);
      } else {
        reject(new Error('No content-length available'));
      }
      xhr.abort();
    };
    xhr.send();
  }
);

【讨论】:

    【解决方案4】:

    1) 如果只需要标头,则应始终首选“HEAD”而不是“GET”,因为有一个简单但不广为人知的细节:即使您使用“GET”并在 readyState === 2 时立即中止(如其他答案建议),您不仅会收到标题,而且会收到完整的第一块信息(标题+正文的一部分),这些信息的大小可能会有所不同,但通常传输大小至少会不必要地增加一倍。使用 'HEAD' 代替,您可以确保只传输标头。

    2) Content-Length 标头必须由“Access-Control-Expose-Headers”公开,以便客户端可访问。如果您正在处理多个源资源并且您不确定 Content-Length 是否已公开,为了防止异常,您可以在事件处理程序中检查这一点,就像这样(或其他许多不同的方式):

    let contentLength = null;
    
    if (checkHeaders(e.target, ['*','Content-Length'])) {
        // YOU CAN ACCESS HEADER
        contentLength = parseInt(e.target.getResponseHeader("Content-Length"));
    } else {
        // YOU CAN NOT ACCESS HEADER
        console.log('Content-Length NOT AVAILABLE');
    }
    
    function checkHeaders(request, headers) {
        return (headers.some(function (elem) {
            return (request.getResponseHeader("Access-Control-Expose-Headers").includes(elem));
        }));
    }
    

    3) 应用任何类型的编码时,不禁止 Content-Length 标头(如某些 cmets 中所建议的那样)。但是,请注意 Content-Length 通常是解码主体的大小(即使它不应该)。这可以通过许多不同的方式来防止,但这是服务器端的考虑。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-14
      • 2010-09-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多