Frage

Basically, I want to figure out whether I should download a file using AJAX, depending on how large the filesize is.

I guess this question could also be rephrased as: How do I get only the header of an ajax request?


EDIT: ultima-rat0 in the comments told me of two questions that had already been asked that apparently are the same as this one. They are very similar, but they both want jQuery. I want a non-jQuery solution to this.

War es hilfreich?

Lösung

You can get XHR response header data manually:

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

This function will get the filesize of the requested 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.");
});

Andere Tipps

Sometimes HEAD can act differently than GET so I suggest something like this that aborts the request after getting Content-Length header:

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

If HEAD request is not possible:

The solution Ebrahim did only not work in firefox for me because context-length was not available for aborted request in firefox. So I used 'onprogress' event instead of 'onreadystatechange' event:

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();
  }
);

1) If only headers are needed, 'HEAD' should be always preferred over 'GET' because of a simple but not widely known detail: Even if you use 'GET' and immediately abort on readyState === 2 (as suggested by other answers), you will have already received not only the headers, but the full first chunk of information (headers + part of the body) that can vary in size, but usually transfer size will be at least doubled unnecessarily. Using 'HEAD' instead, you can be sure that only headers will be transferred.

2) Content-Length header must be exposed by 'Access-Control-Expose-Headers' to be accessible client-side. If you are dealing with multiple origin resources and you are not sure if Content-Length has been exposed, to prevent exceptions, you can check that, inside an event handler, like this (or other many different ways):

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 header IS NOT forbidden when any type of encoding is applied (as suggested in some comments). But, be careful that Content-Length will be usually the size of the decoded body (even if it should not). This can be prevented in many different ways, but it is a server-side consideration.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top