關於PHP 如何用 curl 讀取 HTTP chunked 資料,curlchunked
對於 Web 服務器返回的 HTTP chunked 資料, 我們可能希望在每一個 chunk 返回時得到回調, 而不是所有的響應返回後再回調. 例如, 當伺服器是 icomet 的時候.
在 PHP 中使用 curl 代碼如下:
<?php $url = "http://127.0.0.1:8100/stream";$ch = curl_init($url);curl_setopt($ch, CURLOPT_WRITEFUNCTION, 'myfunc');$result = curl_exec($ch);curl_close($ch);function myfunc($ch, $data){$bytes = strlen($data);// 處理 datareturn $bytes;}
但是, 這裡有一個問題. 對於一個 chunk, 回呼函數可能會被調用多次, 每一次大概是 16k 的資料. 這顯然不是我們希望得到的. 因為 icomet 的一個 chunk 是以 "\n" 結尾, 所以回呼函數可以做一下緩衝.
function myfunc($ch, $data){$bytes = strlen($data);static $buf = '';$buf .= $data;while(1){$pos = strpos($buf, "\n");if($pos === false){break;}$data = substr($buf, 0, $pos+1);$buf = substr($buf, $pos+1);// 處理 data}}
下面給大家介紹下chunked php使用fsockopen讀取分段資料(transfer-encoding: chunked)
使用fsockopen讀取資料時遇到了一個神奇的問題,具體情況如下:
讀取地址:http://blog.maxthon.cn/?feed=rss2
讀取代碼:
<?php$fp = fsockopen("blog.maxthon.cn", 80, $errno, $errstr, 30);if (!$fp) {echo "$errstr ($errno)
\n";} else {$out = "GET /?feed=rss2 HTTP/1.1\r\n";$out .= "Host: blog.maxthon.cn\r\n";$out .= "Connection: Close\r\n\r\n";fwrite($fp, $out);while (!feof($fp)) {echo fgets($fp, 128);}fclose($fp);}?>
返回http內容:
Date: Mon, 29 Mar 2010 10:16:13 GMTServer: Apache/2.2.8 (Unix) mod_ssl/2.2.8 OpenSSL/0.9.8b PHP/5.2.6X-Powered-By: PHP/5.2.6X-Pingback: http://blog.maxthon.cn/xmlrpc.phpLast-Modified: Wed, 03 Mar 2010 03:13:41 GMTETag: "8f16b619f32188bde3bc008a60c2cc11"Keep-Alive: timeout=15, max=120Connection: Keep-AliveTransfer-Encoding: chunkedContent-Type: text/xml; charset=UTF-822de<?xml version="1.0" encoding="UTF-8"?>2009年12月31日1711.......1fe8<p>2009年12月31日<br />1711</p></pre><p>請注意上面那些標紅的4個字元,它們每隔一段資料就會出現一次,但是用其他的方法如curl,file_get_contents等取回的資料則沒有這些玩意。換成其他的網站來抓取,也只是少數的網站會出現這種情況,多方搜尋無解後,我無意中看到了上面返回頭中有這麼一個聲明:Transfer-Encoding: chunked,而常見的Content-lenght欄位沒有了。這個聲明的大致的意思是傳輸編碼為分段方式。<br /></p><p>在Google上搜尋該關鍵詞,在維基百科上找到對這個聲明的解釋(由於沒有中文版,我只能自己按照意思翻譯):<br /></p><pre class="code">Chunked Transfer Encoding is a mechanism that allows HTTP messages to be split in several parts. This can be applied to both HTTP requests (from client to server) and HTTP responses (from server to client)</pre><br /><p>分塊傳輸編碼是一種機制,允許將HTTP訊息分成幾個部分傳輸。同時適用於HTTP請求(從用戶端到伺服器)和 HTTP響應(從伺服器到用戶端)<br /></p><p>For example, let us consider the way in which an HTTP server may transmit data to a client application (usually a web browser). Normally, data delivered in HTTP responses is sent in one piece, whose length is indicated by the Content-Length header field. The length of the data is important, because the client needs to know where the response ends and any following response starts. With chunked encoding, however, the data is broken up into a series of blocks of data and transmitted in one or more "chunks" so that a server may start sending data before it knows the final size of the content that it's sending. Often, the size of these blocks is the same, but this is not always the case.<br /></p><p>例如,讓我們考慮HTTP伺服器可將資料轉送到用戶端應用程式(通常是一個網路瀏覽器)使用哪些方式。通常情況下,在HTTP響應資料是按照一整塊發送給用戶端的,資料的長度是由Content - Length頭域表示。資料的長度很重要,因為客戶需要知道在哪裡響應結束和後面的響應何時啟動。而使用Chunked編碼方式,不管怎樣,資料都會分割成一系列的資料區塊和一個或多個轉寄的“塊”,因此伺服器在知道內容的長度之前,就可以開始發送資料後。通常情況下,這些資料區塊的大小是一樣的,但也並不是絕對的。<br /></p><p>大概意思瞭解後,我們來看例子:<br /></p><p>Chunked編碼使用若干個Chunk串聯而成,由一個標明長度為0的chunk標示結束。每個Chunk分為頭部和本文兩部分,頭部內容指定下一段本文的字元總數(十六進位的數字)和數量單位(一般不寫),本文部分就是指定長度的實際內容,兩部分之間用斷行符號換行(CRLF)隔開。在最後一個長度為0的Chunk中的內容是稱為footer的內容,是一些附加的Header資訊(通常可以直接忽略)。具體的Chunk編碼格式如下:<br /></p><p>編過碼的響應內容:</p><p>HTTP/1.1 200 OK<br />Content-Type: text/plain<br />Transfer-Encoding: chunked</p><p>25<br /></p><p>這是第一段資料</p><p>1A<br /></p><p>然後這是第二段資料<br /></p><p>0</p><p>解碼的資料:<br /></p><p>這是第一段內容,然後這是第二段資料<br /></p><p>情況搞清楚了,那麼我們怎麼來解碼這個編碼後的資料呢?<br /></p><p>在php官方手冊fsockopen函數下面的評論中,已經有很多人提出瞭解決方法<br /></p><p><strong>方法1.</strong><br /></p><pre class="code"><?phpfunction unchunk($result) {return preg_replace_callback('/(?:(?:\r\n|\n)|^)([0-9A-F]+)(?:\r\n|\n){1,2}(.*?)'.'((?:\r\n|\n)(?:[0-9A-F]+(?:\r\n|\n))|$)/si',create_function('$matches','return hexdec($matches[1]) == strlen($matches[2]) ? $matches[2] : $matches[0];'),$result);}</pre><p><strong>方法二.</strong><br /></p><pre class="code">function unchunkHttp11($data) {$fp = 0;$outData = "";while ($fp < strlen($data)) {$rawnum = substr($data, $fp, strpos(substr($data, $fp), "\r\n") + 2);$num = hexdec(trim($rawnum));$fp += strlen($rawnum);$chunk = substr($data, $fp, $num);$outData .= $chunk;$fp += strlen($chunk);}return $outData;}</pre><p><span style="color: #ff0000">注意:這兩個函數的參數都是返回的http未經處理資料(包括頭)</span></p><!--endmain--><h4>您可能感興趣的文章:</h4><ul><li>PHP的cURL庫功能簡介 抓取網頁、POST資料及其他</li><li>php中使用Curl、socket、file_get_contents三種方法POST提交資料</li><li>PHP下使用CURL方式POST資料至API介面的代碼</li><li>解析PHP 使用curl提交json格式資料</li><li>php使用curl發送json格式資料執行個體</li><li>php curl類比post提交資料樣本</li><li>php使用curl和Regex抓取網頁資料樣本</li><li>PHP函數分享之curl方式取得資料、類比登陸、POST資料</li></ul></p><p align="left"><span id="url" itemprop="url">http://www.bkjia.com/PHPjc/1104336.html</span><span id="indexUrl" itemprop="indexUrl">www.bkjia.com</span><span id="isOriginal" itemprop="isOriginal">true</span><span id="isBasedOnUrl" itemprop="isBasedOnUrl">http://www.bkjia.com/PHPjc/1104336.html</span><span id="genre" itemprop="genre">TechArticle</span><span id="description" itemprop="description">關於PHP 如何用 curl 讀取 HTTP chunked 資料,curlchunked 對於 Web 服務器返回的 HTTP chunked 資料, 我們可能希望在每一個 chunk 返回時得到回調, 而不...</span></p><li ><i class="layui-icon">