php下載遠程圖片方法總結(curl手動解析header)curl跳轉問題解決


常用方法一般有:、

file_get_contents

file_put_contents

readfile($file) //效率很高。

一般代碼:

/**
 * 抓取遠程圖片
 *
 * @param string $url 遠程圖片路徑
 * @param string $filename 本地存儲文件名
 */
function grabImage($url, $filename = '') {
    if($url == '') {
        return false; //如果 $url 為空則返回 false;
    }
    $ext_name = strrchr($url, '.'); //獲取圖片的擴展名
    if($ext_name != '.gif' && $ext_name != '.jpg' && $ext_name != '.bmp' && $ext_name != '.png') {
        return false; //格式不在允許的范圍
    }
    if($filename == '') {
        $filename = time().$ext_name; //以時間戳另起名
    }
    //開始捕獲
    ob_start();
    readfile($url); $img_data = ob_get_contents();
    ob_end_clean();
    $size = strlen($img_data);
    $local_file = fopen($filename , 'a');
    fwrite($local_file, $img_data); fclose($local_file);
    return $filename;
}

我一個網址:http://www.njphp.cn/uc_server/avatar.php?uid=1&size=middle 測試一下,發現什么都沒有輸出。

為什么沒有輸出:因為grabImage函數檢測url發現擴展名不是圖片格式就返回false了。我們可以不檢測后綴名就可以了。

讓我們看看http://www.njphp.cn/uc_server/avatar.php?uid=1&size=middle

這個網址,這個網址不是直接輸出圖片流,而是重定向了。

在瀏覽器測試發現

Status Code:

301 Moved Permanently
返回的301狀態嗎,永久性重定向到另一個頁面,也就是圖片的真實地址:
http://www.njphp.cn/uc_server/data/avatar/000/00/00/01_avatar_middle.jpg。
上面的代碼無法處理重定向這種形式。

上面的函數有幾個缺點:

1.不能自動識別圖片后綴名(很多圖片的url並不指向一個靜態圖片地址,而是直接將圖片流輸出到客戶端)

2.不支持圖片url的302跳轉  (只是return false罷了,readfile和file_get_contents是讀取最終的文件的,即支持重定向)

這個函數並不符合本人項目的需求,於是花了點時間自己寫了一個下載函數,此函數支持:

1.靜態圖片下載

2.服務端直接輸出圖片流下載

3.服務端使用302跳轉到真實圖片地址的下載(可限定跳轉次數) 

函數代碼如下:

/**
     * 下載遠程圖片
     * @param string $url 圖片的絕對url
     * @param string $filepath 文件的完整路徑(包括目錄,不包括后綴名,例如/www/images/test) ,此函數會自動根據圖片url和http頭信息確定圖片的后綴名
     * @return mixed 下載成功返回一個描述圖片信息的數組,下載失敗則返回false
     */
    function downloadImage($url, $filepath) {
        //服務器返回的頭信息
        $responseHeaders = array();
        //原始圖片名
        $originalfilename = '';
        //圖片的后綴名
        $ext = '';
        $ch = curl_init($url);
        //設置curl_exec返回的值包含Http頭
        curl_setopt($ch, CURLOPT_HEADER, 1);
        //設置curl_exec返回的值包含Http內容
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);        
        //設置抓取跳轉(http 301,302)后的頁面
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
        //設置最多的HTTP重定向的數量
        curl_setopt($ch, CURLOPT_MAXREDIRS, 2); //服務器返回的數據(包括http頭信息和內容)
        $html = curl_exec($ch);
        //獲取此次抓取的相關信息
        $httpinfo = curl_getinfo($ch);
        curl_close($ch);
        if ($html !== false) {
            //分離response的header和body,由於服務器可能使用了302跳轉,所以此處需要將字符串分離為 2+跳轉次數 個子串
            $httpArr = explode("\r\n\r\n", $html, 2 + $httpinfo['redirect_count']); //最后一個參數可選。規定所返回的數組元素的最大數目。
            //倒數第二段是服務器最后一次response的http頭
            $header = $httpArr[count($httpArr) - 2];
            //倒數第一段是服務器最后一次response的內容
            $body = $httpArr[count($httpArr) - 1];
            $header.="\r\n";

            //獲取最后一次response的header信息
            preg_match_all('/([a-z0-9-_]+):\s*([^\r\n]+)\r\n/i', $header, $matches);
            if (!empty($matches) && count($matches) == 3 && !empty($matches[1]) && !empty($matches[1])) {
                for ($i = 0; $i < count($matches[1]); $i++) {
                    if (array_key_exists($i, $matches[2])) {
                        $responseHeaders[$matches[1][$i]] = $matches[2][$i];
                    }
                }
            }
            //獲取圖片后綴名
            if (0 < preg_match('{(?:[^\/\\\\]+)\.(jpg|jpeg|gif|png|bmp)$}i', $url, $matches)) {
                $originalfilename = $matches[0];
                $ext = $matches[1];
            } else {
                if (array_key_exists('Content-Type', $responseHeaders)) {
                    if (0 < preg_match('{image/(\w+)}i', $responseHeaders['Content-Type'], $extmatches)) {
                        $ext = $extmatches[1];
                    }
                }
            }
            //保存文件
            if (!empty($ext)) {
                $filepath .= ".$ext";
                //如果目錄不存在,則先要創建目錄
                CFiles::createDirectory(dirname($filepath));
                $local_file = fopen($filepath, 'w');
                if (false !== $local_file) {
                    if (false !== fwrite($local_file, $body)) {
                        fclose($local_file);
                        $sizeinfo = getimagesize($filepath);
                        return array('filepath' => realpath($filepath), 'width' => $sizeinfo[0], 'height' => $sizeinfo[1], 'orginalfilename' => $originalfilename, 'filename' => pathinfo($filepath, PATHINFO_BASENAME));
                    }
                }
            }
        }
        return false;
    }

 解決跳轉問題設置:

//設置抓取跳轉(http 301,302)后的頁面
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
        //設置最多的HTTP重定向的數量
        curl_setopt($ch, CURLOPT_MAXREDIRS, 2);

值得注意的是
我們獲取了所有跳轉的頁面的header:
$httpinfo=curl_getinfo($ch);
如何得到最后一個頁面的header,用到了explode函數。
explode(分隔符,字符串,limit);
//最后一個參數可選。規定所返回的數組元素的最大數目。假設我們請求的頁面有2次跳轉
a->b->c

print_r ($httpinfo)結果類似:

Array
(
    [url] => http://c.php
    [content_type] => text/html
    [http_code] => 200
    [header_size] => 602
    [request_size] => 230
    [filetime] => -1
    [ssl_verify_result] => 0 [redirect_count] => 2
    [total_time] => 0.281
    [namelookup_time] => 0
    [connect_time] => 0
    [pretransfer_time] => 0
    [size_upload] => 0
    [size_download] => 0
    [speed_download] => 0
    [speed_upload] => 0
    [download_content_length] => -1
    [upload_content_length] => 0
    [starttransfer_time] => 0.047
    [redirect_time] => 0.234
    [certinfo] => Array
        (
        )

    [primary_ip] => ::1
    [primary_port] => 80
    [local_ip] => ::1
    [local_port] => 50768
    [redirect_url] => 
)

里面有一個direct_count記錄跳轉的次數。

我們可以利用上面兩點從$html=curl_exec($ch);提取最后一個頁面的header信息。

     //分離response的header和body,由於服務器可能使用了302跳轉,所以此處需要將字符串分離為 2+跳轉次數 個子串
            $httpArr = explode("\r\n\r\n", $html, 2 + $httpinfo['redirect_count']);
            //倒數第二段是服務器最后一次response的http頭
            $header = $httpArr[count($httpArr) - 2];
            //倒數第一段是服務器最后一次response的內容
            $body = $httpArr[count($httpArr) - 1];
            $header.="\r\n";
       print $header;

每個header信息最后都有一個\r\n\r\n,如:

reponse header:

HTTP/1.0 200 OK
Date: Wed, 07 Aug 2013 08:15:21 GMT
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
X-Powered-By: PHP/5.2.17
Content-Type: text/html; charset=UTF-8
Content-Encoding: gzip
Vary: Accept-Encoding
X-Cache: MISS from tesaasst.abc.com
X-Cache-Lookup: MISS from tesaasst.abc.com:80
Via: 1.0 tesaasst.abc.com (squid/3.0.STABLE20)
Connection: close
有一個換行符

a->b->c會產生如下形式:

a header \r\n\r\n bheader \r\n \r\n  cheader \r\n\r\n cbody.

分成了

2 + $httpinfo['redirect_count']片段。
最后一個為body,倒數第二個為header。

注意,我們想要打印最后一個頁面的header,還要加上

$header.="\r\n";
符合http 規范。

CURLOPT_FOLLOWLOCATION

  TRUE to follow any "Location: " header that the server sends as part of the HTTP header (note this is recursive, PHP will follow as many "Location: " headers that it is sent, unless CURLOPT_MAXREDIRS is set).默認true

CURLOPT_MAXREDIRS

  The maximum amount of HTTP redirections to follow. Use this option alongside CURLOPT_FOLLOWLOCATION.

CURLOPT_AUTOREFERER

  TRUE to automatically set the Referer: field in requests where it follows a Location: redirect.

CURLOPT_AUTOREFERER  :curl 會自動添加 Referer header 在每一個跳轉鏈接,也就是一跟到底。默認true.

參考了:

http://www.cnblogs.com/helloprogram/archive/2012/03/25/2416492.html

http://www.groad.net/bbs/read.php?tid-4455.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM