php 獲取 http 響應頭 get_headers 方法的一個細節

Ceelog發表於2017-05-23

背景

在 Web 後端開發過程中,經常需要確認一個遠端網路檔案是否存在,或者檢視檔案的大小。

如果直接將檔案下載下來,當然可以在本地計算檔案大小,但是未免要消耗大量時間和頻寬資源。

有沒有辦法只獲取檔案大小,但又不用去下載呢?

答案是:可以。

HTTP 協議

http response 報文中,主要分為兩大塊:

  • 響應頭部 response header
  • 響應內容 response body

以瀏覽器請求一張圖片為例:

php 獲取 http 響應頭 get_headers 方法的一個細節

可以看到,響應頭包含一個內容長度的欄位:

Content-Length:5567複製程式碼

所以,只要獲取到 http 響應頭,就可以知道網路檔案的大小了。

http 協議中專門定義了 HEAD 請求方法,告知伺服器只返回響應頭資訊,不用返回響應內容。

請求示例:

curl -v -I 'http://upload.jianshu.io/users/upload_avatars/19687/a0ac666907b7?imageMogr2/auto-orient/strip|imageView2/1/w/240/h/240'複製程式碼

請求過程和響應:

> HEAD /users/upload_avatars/19687/a0ac666907b7?imageMogr2/auto-orient/strip|imageView2/1/w/240/h/240 HTTP/1.1
> User-Agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.13.1.0 zlib/1.2.3 libidn/1.18 libssh2/1.2.2
> Host: upload.jianshu.io
> Accept: */*
> 
< HTTP/1.1 200 OK
< Date: Tue, 23 May 2017 12:21:54 GMT
< Server: openresty
< Content-Type: image/jpeg
< Content-Length: 5567
< Accept-Ranges: bytes複製程式碼

PHP

令人欣喜的是,php 已經內建了獲取網路資源響應頭的方法了:

get_headers();複製程式碼

執行命令:

php -r "print_r(get_headers('http://upload.jianshu.io/users/upload_avatars/19687/a0ac666907b7?imageMogr2/auto-orient/strip|imageView2/1/w/240/h/240'));"複製程式碼

列印結果:

Array
(
    [0] => HTTP/1.1 200 OK
    [1] => Date: Tue, 23 May 2017 13:35:38 GMT
    [2] => Server: openresty
    [3] => Content-Type: image/jpeg
    [4] => Content-Length: 5567
    [5] => Accept-Ranges: bytes
)複製程式碼

問題

看起來 phpget_headers 方法很好的執行了 http 協議,只獲取了 header 資訊。

但是,Nginx日誌卻顯示 get_headers 方法用的是 GET 方法,並不是 HEAD

127.0.0.1 - - [23/May/2017:21:51:22 +0800] GET /test.html HTTP/1.0 "200" ......複製程式碼

事實上,在使用 get_headers之前,你得手動指定用 HEAD 方法:

<?php
// By default get_headers uses a GET request to fetch the headers. If you want to send a HEAD request instead, you can do so using a stream context:
stream_context_set_default(
    array(
        'http' => array(
            'method' => 'HEAD'
        )
    )
);
$headers = get_headers('http://example.com/test.html');複製程式碼

Nginx 日誌:

127.0.0.1 - - [23/May/2017:21:51:22 +0800] HEAD /test.html HTTP/1.0 "200" ......複製程式碼

總結

在使用 get_headers 獲取響應頭的時候,預設用的是 GET 方法,這就意味著除了返回頭資訊外,還有響應內容,這不就是直接下載了麼?

事實上,在獲取到頭資訊和少量的響應內容後,get_headers 方法就會主動關閉 tcp 連線,這樣就不會把整個檔案下載到記憶體了。

但是,我們的需求畢竟是隻獲取響應頭,所以,在使用 get_headers 的時候建議手動指定 HEAD 請求方法。

還有問題?聯絡作者微信/微博 @Ceelog

相關文章