[英]How to copy very large files from URL to server via PHP?
我使用以下代碼將文件從外部服務器(通過 URL 的任何服務器)復制/下載到我托管的 web 服務器(默認設置為 Dreamhost 共享主機)。
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<form method="post" action="copy.php">
<input type="submit" value="click" name="submit">
</form>
</body>
</html>
<!-- copy.php file contents -->
<?php
function chunked_copy() {
# 1 meg at a time, adjustable.
$buffer_size = 1048576;
$ret = 0;
$fin = fopen("http://www.example.com/file.zip", "rb");
$fout = fopen("file.zip", "w");
while(!feof($fin)) {
$ret += fwrite($fout, fread($fin, $buffer_size));
}
fclose($fin);
fclose($fout);
return $ret; # return number of bytes written
}
if(isset($_POST['submit']))
{
chunked_copy();
}
?>
然而,function 大約在下載完 2.5GB(有時是 2.3GB,有時是 2.7GB 等)文件后停止運行。 每次執行此 function 時都會發生這種情況。較小的文件 (<2GB) 很少出現此問題。 我相信源沒有問題,因為我單獨將文件完美地下載到我的家用電腦上。
有人可以解決這個問題並向我解釋嗎? 我對編程很陌生。
還,
file_put_contents("Tmpfile.zip", fopen("http://example.com/file.zip", 'r'));
也表現出類似的症狀。
我認為問題可能是許多運行PHP腳本的服務器上的30秒超時。
通過cron或shell運行的PHP腳本不會有此問題,因此也許您可以找到一種方法來實現。
或者,您可以將set_time_limit([desired time])添加到代碼的開頭。
也許您可以嘗試curl下載文件。
function downloadUrlToFile($url, $outFileName)
{
//file_put_contents($xmlFileName, fopen($link, 'r'));
//copy($link, $xmlFileName); // download xml file
if(is_file($url)) {
copy($url, $outFileName); // download xml file
} else {
$options = array(
CURLOPT_FILE => fopen($outFileName, 'w'),
CURLOPT_TIMEOUT => 28800, // set this to 8 hours so we dont timeout on big files
CURLOPT_URL => $url
);
$ch = curl_init();
curl_setopt_array($ch, $options);
curl_exec($ch);
curl_close($ch);
}
}
說明:也許。 Remidy:可能不會。
這可能是由於PHP的限制所致: 關於文件大小函數的手冊在返回值部分中提到:
注意:因為PHP的整數類型是帶符號的,並且許多平台使用32位整數,所以某些文件系統函數可能會為大於2GB的文件返回意外結果。
看來, fopen
功能可能會引起問題,因為兩點意見( 1 , 2 )加入(雖然改裝成向下)關於這個問題。
似乎您需要從源代碼編譯PHP(帶有CFLAGS="-D_FILE_OFFSET_BITS=64"
標志)以啟用大文件(> 2GB),但是這可能會破壞其他功能。
由於您使用的是共享的查詢:我想您很走運。
抱歉...
由於問題是在(尚)未知且未定義的文件大小下發生的,因此最好嘗試解決方法。 如果您只是關閉並在一定數量的字節后重新打開輸出文件,該怎么辦?
function chunked_copy() {
# 1 meg at a time, adjustable.
$buffer_size = 1048576;
# 1 GB write-chuncks
$write_chuncks = 1073741824;
$ret = 0;
$fin = fopen("http://www.example.com/file.zip", "rb");
$fout = fopen("file.zip", "w");
$bytes_written = 0;
while(!feof($fin)) {
$bytes = fwrite($fout, fread($fin, $buffer_size));
$ret += $bytes;
$bytes_written += $bytes;
if ($bytes_written >= $write_chunks) {
// (another) chunck of 1GB has been written, close and reopen the stream
fclose($fout);
$fout = fopen("file.zip", "a"); // "a" for "append"
$bytes_written = 0; // re-start counting
}
}
fclose($fin);
fclose($fout);
return $ret; # return number of bytes written
}
重新打開應該使用附加模式,它將在文件末尾放置寫指針(沒有讀指針),而不覆蓋先前寫的字節。
這不會解決任何操作系統級別或文件系統級別的問題,但可以解決寫入文件時PHP內部的任何計數問題。
也許這個技巧也可以(或應該)應用於閱讀端,但是我不確定您是否可以在下載中執行搜索操作。
請注意,任何整數溢出(如果您使用的是32位,則超出2147483647)都應通過強制轉換為float來透明解決,因此這不是問題。
編輯 :計算實際寫入的字節數,而不是塊大小
30秒后您可能會超時,這可能是由PHP引起的(默認情況下max_execution_time
= 30s)。 您可以嘗試將其設置為更長的時間:
ini_set('max_execution_time', '300');
但是,有一些警告:
如果腳本在安全模式下運行,則無法使用ini_set
設置max_execution_time
(我無法找到Dreamhost在共享主機中是打開還是關閉安全模式,您需要詢問它們,或者只是嘗試一下)。
Web服務器也可能具有執行限制。 Apache的默認值為300s(也有IIS,但考慮到Dreamhost提供了“完整的unix外殼”,Apache比IIS更有可能)。 但是,如果文件大小為5GB,這應該會對您有所幫助。
這是我找到的下載超大文件的最佳方式:快速且不需要大量 memory。
public function download_large_file(string $url, string $dest)
{
ini_set('memory_limit', '3000M');
ini_set('max_execution_time', '0');
try {
$handle1 = fopen($url, 'r');
$handle2 = fopen($dest, 'w');
stream_copy_to_stream($handle1, $handle2);
fclose($handle1);
fclose($handle2);
return true;
}
catch(\Exception $e) {
return $e->getMessage();
}
return true;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.