如何从HTTP头中获取文件大小

74

我想在下载文件之前获得http:/.../file的大小。该文件可以是网页、图像或媒体文件。这是否可以通过HTTP标头完成?如何仅下载文件的HTTP标头?

5个回答

103

是的,假设你所访问的HTTP服务器支持/允许这样做:

public long GetFileSize(string url)
{
    long result = -1;

    System.Net.WebRequest req = System.Net.WebRequest.Create(url);
    req.Method = "HEAD";
    using (System.Net.WebResponse resp = req.GetResponse())
    {
        if (long.TryParse(resp.Headers.Get("Content-Length"), out long ContentLength))
        {
            result = ContentLength;
        }
    }

    return result;
}
如果不允许使用HEAD方法,或者服务器回复中没有Content-Length头,则确定服务器上内容的大小的唯一方法是下载它。由于这种方法不太可靠,所以大多数服务器都会包含此信息。

12
如果你使用 using,它会自动处理资源的释放。详见:http://msdn.microsoft.com/zh-cn/library/yh598w02(v=vs.110).aspx - justderb
3
另外需要注意的是,如果您要处理非常大的文件,使用 int 类型可能不够,您需要使用 long ContentLength; 并且使用 long.TryParse(xxx) 来支持超过2.14GB大小返回值。 - Preston
启用HTTP压缩不会影响实际文件大小吗? - Justin
我使用这种方法来了解此链接的大小:http://ipv4.download.thinkbroadband.com/200MB.zip,但是却收到了403错误!为什么? - Behzad

31

可以使用 HTTP 头部完成吗?

是的,这是正确的方法。如果提供了信息,则以头部的形式作为 Content-Length 存在。但请注意,不一定总是如此。

只下载头部可以使用 HEAD 请求而不是 GET 请求。以下代码可能会有所帮助:

HttpWebRequest req = (HttpWebRequest)WebRequest.Create("http://example.com/");
req.Method = "HEAD";
long len;
using(HttpWebResponse resp = (HttpWebResponse)(req.GetResponse()))
{
    len = resp.ContentLength;
}

注意在HttpWebResponse对象上的内容长度属性 - 无需手动解析Content-Length标头。


resp.ContentLength 不会给你感兴趣的文件的大小,而是只会给你 HEAD 响应的长度。 - Adam Nofsinger
1
@Adam 不是的。文档中说:“ContentLength 属性包含与响应返回的 Content-Length 标头的值。” - Konrad Rudolph
请确保调用resp.Close(),否则在同时进行多个请求时可能会遇到超时错误(我的第三个请求在foreach循环中超时,通过关闭每个响应来解决了这个问题)。 - Eric Smith
3
实际上,你应该在这里使用一个 Using 块或实现可处理模式来显式管理资源的生命周期。手动调用 Close 是不够的,除非你确保它 始终 发生,即使出现错误也是如此。 - Konrad Rudolph
@KonradRudolph 你说得对。在我测试的时候,调用Close()修复了我的错误,但是使用using块才是正确的做法。唉。 - Eric Smith
@KonradRudolph,FYI,ContentLength返回一个long。虽然不是什么大问题,但以防万一你想修复它。 - gunr2171

4
请注意,不是每个服务器都接受HTTP HEAD请求。获取文件大小的另一种方法是向服务器发出HTTP GET调用,仅请求文件的一部分以保持响应较小,并从响应内容头返回的元数据中检索文件大小。
可以使用标准的System.Net.Http.HttpClient来实现这一点。通过在请求消息头上设置字节范围来请求部分内容,如下所示:
    request.Headers.Range = new RangeHeaderValue(startByte, endByte)

服务器响应包含请求范围和整个文件大小的消息。该信息在响应内容头(response.Content.Header)中返回,使用键“Content-Range”。

以下是响应消息内容头中内容范围的示例:

    {
       "Key": "Content-Range",
       "Value": [
         "bytes 0-15/2328372"
       ]
    }

在这个例子中,头部值意味着响应包含 0 到 15 字节(一共 16 个字节),而文件总共有 2,328,372 个字节。
以下是此方法的示例实现:
public static class HttpClientExtensions
{
    public static async Task<long> GetContentSizeAsync(this System.Net.Http.HttpClient client, string url)
    {
        using (var request = new System.Net.Http.HttpRequestMessage(System.Net.Http.HttpMethod.Get, url))
        {
            // In order to keep the response as small as possible, set the requested byte range to [0,0] (i.e., only the first byte)
            request.Headers.Range = new System.Net.Http.Headers.RangeHeaderValue(from: 0, to: 0);

            using (var response = await client.SendAsync(request))
            {
                response.EnsureSuccessStatusCode();

                if (response.StatusCode != System.Net.HttpStatusCode.PartialContent) 
                    throw new System.Net.WebException($"expected partial content response ({System.Net.HttpStatusCode.PartialContent}), instead received: {response.StatusCode}");

                var contentRange = response.Content.Headers.GetValues(@"Content-Range").Single();
                var lengthString = System.Text.RegularExpressions.Regex.Match(contentRange, @"(?<=^bytes\s[0-9]+\-[0-9]+/)[0-9]+$").Value;
                return long.Parse(lengthString);
            }
        }
    }
}

不错的解决方案,但并非每个服务器都允许内容范围请求。 - Phani Rithvij

1
WebClient webClient = new WebClient();
webClient.OpenRead("http://stackoverflow.com/robots.txt");
long totalSizeBytes= Convert.ToInt64(webClient.ResponseHeaders["Content-Length"]);
Console.WriteLine((totalSizeBytes));

2
这是一个很好的解决方案,特别是如果您已经在使用WebClient下载文件,并且只想先检查文件长度。 - ScottFoster1000

0
    HttpClient client = new HttpClient(
        new HttpClientHandler() {
            Proxy = null, UseProxy = false
        } // removes the delay getting a response from the server, if you not use Proxy
    );

    public async Task<long?> GetContentSizeAsync(string url) {
        using (HttpResponseMessage responce = await client.GetAsync(url))
            return responce.Content.Headers.ContentLength;
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接