2010-09-02 84 views
51

我正在使用一個腳本(我沒有創建它),它從HTML頁面生成一個pdf文件。問題是現在需要花費很長時間,例如1-2分鐘來處理。據說這最初工作的很好,但在過去的幾周內已經放慢了速度。PHP file_get_contents使用完整的url時速度很慢

該腳本在php腳本上調用file_get_contents,然後將結果輸出到服務器上的HTML文件中,並在該文件上運行pdf生成器應用程序。

我似乎已經將問題縮小到完整url上的file_get_contents調用,而不是本地路徑。

當我使用

$content = file_get_contents('test.txt'); 

它處理幾乎瞬間。然而,如果我使用完整的網址,則需要30-90秒的時間來處理。

它不限於我們的服務器,訪問任何外部網址時速度很慢,如http://www.google.com。我相信腳本會調用完整的url,因爲如果您在本地調用文件,則需要查詢字符串變量,這些變量不起作用。

我也嘗試過fopen,readfilecurl,他們都很慢。任何想法在哪裏尋找解決這個問題?

回答

1

你能嘗試從命令行中獲取服務器上的那個URL嗎?捲曲或wget想到。如果這些以正常速度檢索URL,那麼它不是網絡問題,並且很可能是apache/php安裝程序中的某些內容。

+1

當我嘗試wget從命令行,這也很慢。它正在解決...步驟。服務器上的某種DNS問題? – ecurbh 2010-09-02 17:45:31

+0

可能。嘗試使用'host'或'nslookup'(任何可用的)並嘗試從系統中解析各種不同的主機名。 – 2010-09-02 19:01:43

26

我會使用curl()來獲取外部內容,因爲這比file_get_contents方法快得多。不知道這是否會解決問題,但值得一試。

另請注意,您的服務器速度將影響檢索文件所用的時間。

下面是使用的例子:

$ch = curl_init(); 
curl_setopt($ch, CURLOPT_URL, 'http://example.com/test.txt'); 
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 
$output = curl_exec($ch); 
+0

請問您可以鏈接到比較file_get_contents和捲曲速度的基準嗎? – shamittomar 2010-09-02 18:09:39

+0

@shamittomar,基準各不相同,但一個簡單的谷歌可以拿出一堆不同的結果。 http://stackoverflow.com/questions/555523/file-get-contents-vs-curl-what-has-better-performance就是其中之一。我只知道cURL從我過去使用的各種應用程序中獲得更快的速度。所以這只是個人的經驗,因爲cURL是爲獲取遠程文件的唯一原因而開發的。作爲file_get_contents/fopen開發一般閱讀本地文件。 – 2010-09-02 18:51:45

+0

感謝您的SO問題鏈接。 – shamittomar 2010-09-02 19:31:15

165

注:這已被固定在PHP 5.6.14。即使對於HTTP/1.0請求,現在也會自動發送一個Connection: close標頭。 See commit 4b1dff6

我很難搞清楚file_get_contents腳本緩慢的原因。

通過使用Wireshark進行分析,問題(在我的情況中,也可能是你的問題)是遠程Web服務器在15秒之前沒有關閉TCP連接(即「保持活動」)。

事實上,file_get_contents不會發送「連接」HTTP標頭,所以遠程Web服務器默認情況下認爲它是保持連接狀態,直到15秒才關閉TCP流(它可能不是標準值 - 取決於服務器配置)。

如果HTTP有效負載長度達到響應Content-Length HTTP標頭中指定的長度,則普通瀏覽器會認爲該頁面已完全加載。 File_get_contents不這樣做,這是一個恥辱。

SOLUTION

所以,如果你想知道解決的辦法,那就是:

$context = stream_context_create(array('http' => array('header'=>'Connection: close\r\n'))); 
file_get_contents("http://www.something.com/somepage.html",false,$context); 

的事情僅僅是告訴遠程Web服務器下載時關閉連接完成,因爲file_get_contents不夠智能,無法使用響應Content-Length HTTP標頭自行完成。

+1

感謝您的支持,超級甜美。 – woodscreative 2013-06-13 19:37:28

+20

這個答案應該被接受... – javsmo 2013-08-24 21:02:37

+3

接受,鍍金,鑲框和慶祝。非常感謝。 – Mave 2013-12-02 08:54:47

5

有時候,這是因爲DNS是您的服務器上太慢了,試試這個:

更換

echo file_get_contents('http://www.google.com'); 

$context=stream_context_create(array('http' => array('header'=>"Host: www.google.com\r\n"))); 
echo file_get_contents('http://74.125.71.103', false, $context); 
+0

這是我的情況。 '/ etc/resolv.conf'中配置了兩臺DNS服務器,但第一臺服務器無法訪問。 DNS查找在第一臺服務器上超時,幾秒鐘後跳到第二臺DNS服務器。 – thirdender 2014-05-22 01:33:12

+0

或者乾脆更換 '$結果=的file_get_contents( 'http://google.com',虛假,$背景);' 與 '$ IP =的gethostbyname( 'google.com');'' $ result = file_get_contents(「http:// $ ip」,false,$ context);' – 2016-09-22 09:54:31

2

我有同樣的問題,

唯一對我有用的是設定時間在$options陣列。

$options = array(
    'http' => array(
     'header' => implode($headers, "\r\n"), 
     'method' => 'POST', 
     'content' => '', 
     'timeout' => .5 
    ), 
); 
+0

這是暫停,但我不知道爲什麼。我最好的猜測是OS X上存在IPv6無法關閉的愚蠢行爲。 Curl工作正常,但根據超時情況,file_get_contents將花費60秒以上。注意:此公共接口上禁用了IPv6,因此無法在全局或環回禁用IPv6。 – 2016-09-20 23:09:37

0
$context = stream_context_create(array('http' => array('header'=>'Connection: close\r\n'))); 
$string = file_get_contents("http://localhost/testcall/request.php",false,$context); 

時間:50976毫秒(共5次嘗試avaerage時間)

$ch = curl_init(); 
$timeout = 5; 
curl_setopt($ch, CURLOPT_URL, "http://localhost/testcall/request.php"); 
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout); 
echo $data = curl_exec($ch); 
curl_close($ch); 

時間:46679毫秒(共5次嘗試avaerage時間)

注:request.php用於從mysql數據庫中獲取一些數據。

+0

這是計時數據嗎?如果是這樣,你應該詳細說明一點。 – 2016-01-17 18:18:14

0

我有API通過一個龐大的數據,我使用file_get_contents讀取數據,但前後花了60秒。但是,使用KrisWebDev的解決方案需要大約25秒

$context = stream_context_create(array('https' => array('header'=>'Connection: close\r\n'))); 
file_get_contents($url,false,$context); 
0

我還會考慮與捲曲是你可以「線程」的請求。這對我非常有幫助,因爲我無法訪問目前允許線程化的PHP版本。

例如,我使用file_get_contents從遠程服務器獲取7個圖像,每個請求需要2-5秒。單獨這個過程就會在過程中增加30秒或某些東西,而用戶等待生成PDF。

這從字面上縮短了大約1張圖片的時間。另一個例子,我在之前做的一個時間裏驗證了36個網址。我認爲你說對了。:-)

$timeout = 30; 
    $retTxfr = 1; 
    $user = ''; 
    $pass = ''; 

    $master = curl_multi_init(); 
    $node_count = count($curlList); 
    $keys = array("url"); 

    for ($i = 0; $i < $node_count; $i++) { 
     foreach ($keys as $key) { 
      if (empty($curlList[$i][$key])) continue; 
      $ch[$i][$key] = curl_init($curlList[$i][$key]); 
      curl_setopt($ch[$i][$key], CURLOPT_TIMEOUT, $timeout); // -- timeout after X seconds 
      curl_setopt($ch[$i][$key], CURLOPT_RETURNTRANSFER, $retTxfr); 
      curl_setopt($ch[$i][$key], CURLOPT_HTTPAUTH, CURLAUTH_ANY); 
      curl_setopt($ch[$i][$key], CURLOPT_USERPWD, "{$user}:{$pass}"); 
      curl_setopt($ch[$i][$key], CURLOPT_RETURNTRANSFER, true); 
      curl_multi_add_handle($master, $ch[$i][$key]); 
     } 
    } 

    // -- get all requests at once, finish when done or timeout met -- 
    do { curl_multi_exec($master, $running); } 
    while ($running > 0); 

然後檢查了結果:

  if ((int)curl_getinfo($ch[$i][$key], CURLINFO_HTTP_CODE) > 399 || empty($results[$i][$key])) { 
       unset($results[$i][$key]); 
      } else { 
       $results[$i]["options"] = $curlList[$i]["options"]; 
      } 
      curl_multi_remove_handle($master, $ch[$i][$key]); 
      curl_close($ch[$i][$key]); 

然後關閉文件:

curl_multi_close($master); 
0

我知道這是老問題,但今天我發現它和答案沒爲我工作。我沒有看到任何人說每個IP的最大連接數可能被設置爲1.這樣,您正在執行API請求,並且API正在執行另一個請求,因爲您使用了完整的URL。這就是爲什麼從光盤直接加載的原因。對我來說,解決了一個問題:

if (strpos($file->url, env('APP_URL')) === 0) { 
    $url = substr($file->url, strlen(env('APP_URL'))); 
} else { 
    $url = $file->url; 
} 
return file_get_contents($url);