サーバー上にファイルをレシピエントに送信するためのphpスクリプトがあります。固有のリンクを取得すると、大きなファイルをダウンロードできます。転送に問題があり、ファイルが破損しているか、完了しないことがあります。大きなファイルを送信するより良い方法があるかどうか私は思っています
コード:
$f = fopen(DOWNLOAD_DIR.$database[$_REQUEST['fid']]['filePath'], 'r');
while(!feof($f)){
print fgets($f, 1024);
}
fclose($f);
私は次のような機能を見てきました
http_send_file
http_send_data
しかし、それらが機能するかどうかはわかりません。
この問題を解決する最良の方法は何ですか?
よろしく
erwing
本当に大きなファイルを送信していて、これによる影響を心配している場合は、x-sendfileヘッダーを使用できます。
SOQから sing-xsendfile-with-Apache-php 、howto blog.adaniels.nl:how-i-php-x-sendfile /
cURL 、 のようなもう少し専門的なものを利用できない、または利用したくない場合は、ファイルのチャンク化がPHPで最も速く/最も簡単な方法です。 _mod-xsendfile
_ on Apache または一部の 専用スクリプト 。
_$filename = $filePath.$filename;
$chunksize = 5 * (1024 * 1024); //5 MB (= 5 242 880 bytes) per one chunk of file.
if(file_exists($filename))
{
set_time_limit(300);
$size = intval(sprintf("%u", filesize($filename)));
header('Content-Type: application/octet-stream');
header('Content-Transfer-Encoding: binary');
header('Content-Length: '.$size);
header('Content-Disposition: attachment;filename="'.basename($filename).'"');
if($size > $chunksize)
{
$handle = fopen($filename, 'rb');
while (!feof($handle))
{
print(@fread($handle, $chunksize));
ob_flush();
flush();
}
fclose($handle);
}
else readfile($path);
exit;
}
else echo 'File "'.$filename.'" does not exist!';
_
richnetapps.com / NeedBee から移植されました。最大許容メモリ制限が_1G
_に設定されていても、ダウンロードされたファイルサイズの5倍の200 MBファイルでテストされ、readfile()
が終了しました。
ところで、私はこれをファイル_>2GB
_でもテストしましたが、PHPは最初にファイルの_2GB
_を書き込んでから接続を切断することができました。ファイル関連の関数(fopen、 fread、fseek)はINTを使用するため、最終的に_2GB
_の制限に達します。上記の解決策(つまり、_mod-xsendfile
_)がこの場合の唯一のオプションのようです。
[〜#〜] edit [〜#〜]:自分を作る100% ファイルが_utf-8
_に保存されていること。省略した場合、ダウンロードしたファイルが破損します。これは、このソリューションがprint
を使用してファイルのチャンクをブラウザーにプッシュするためです。
最善の解決策は、lightyまたはApacheに依存することですが、PHPの場合は PEARのHTTP_Download を使用します(ホイールなどを再発明する必要はありません)。
intro/usage docs を参照してください。
実際のファイルへのシンボリックリンクを作成し、ダウンロードリンクがシンボリックリンクを指すようにします。次に、ユーザーがDLリンクをクリックすると、実際のファイルからファイルがダウンロードされますが、シンボリックリンクから名前が付けられます。シンボリックリンクの作成には数ミリ秒かかります。ファイルを新しい名前にコピーしてそこからダウンロードしようとしています。
例えば:
<?php
// validation code here
$realFile = "Hidden_Zip_File.Zip";
$id = "UserID1234";
if ($_COOKIE['authvalid'] == "true") {
$newFile = sprintf("myzipfile_%s.Zip", $id); //creates: myzipfile_UserID1234.Zip
system(sprintf('ln -s %s %s', $realFile, $newFile), $retval);
if ($retval != 0) {
die("Error getting download file.");
}
$dlLink = "/downloads/hiddenfiles/".$newFile;
}
// rest of code
?>
<a href="<?php echo $dlLink; ?>Download File</a>
Go Daddyが2分30秒ほど後にスクリプトの実行を停止するので、これは私がやったことです。
その後、CRONジョブを設定して、定期的にシンボリックリンクを削除できます。
このプロセス全体がファイルをブラウザに送信し、スクリプトではないため、実行時間は問題ではありません。
ファイルをダウンロードするための最も簡単な方法は、ファイルを一時的な場所に置き、通常のHTTP経由でダウンロードできる一意のURLを与えることです。
これらのリンクを生成する一部として、X時間以上経過したファイルを削除することもできます。
これをいくつかのプロジェクトで使用してきましたが、これまでのところ非常にうまく機能しています。
/**
* Copy a file's content to php://output.
*
* @param string $filename
* @return void
*/
protected function _output($filename)
{
$filesize = filesize($filename);
$chunksize = 4096;
if($filesize > $chunksize)
{
$srcStream = fopen($filename, 'rb');
$dstStream = fopen('php://output', 'wb');
$offset = 0;
while(!feof($srcStream)) {
$offset += stream_copy_to_stream($srcStream, $dstStream, $chunksize, $offset);
}
fclose($dstStream);
fclose($srcStream);
}
else
{
// stream_copy_to_stream behaves() strange when filesize > chunksize.
// Seems to never hit the EOF.
// On the other handside file_get_contents() is not scalable.
// Therefore we only use file_get_contents() on small files.
echo file_get_contents($filename);
}
}
Webサーバーとしてlighttpdを使用している場合、安全なダウンロードの代替手段は ModSecDownload を使用することです。サーバーの設定が必要ですが、PHPスクリプトではなく、Webサーバーにダウンロード自体を処理させます。
ダウンロードURLの生成は(ドキュメントから取得した)そのようになり、もちろん承認されたユーザーに対してのみ生成できます。
_<?php
$secret = "verysecret";
$uri_prefix = "/dl/";
# filename
# please note file name starts with "/"
$f = "/secret-file.txt";
# current timestamp
$t = time();
$t_hex = sprintf("%08x", $t);
$m = md5($secret.$f.$t_hex);
# generate link
printf('<a href="%s%s/%s%s">%s</a>',
$uri_prefix, $m, $t_hex, $f, $f);
?>
_
もちろん、ファイルのサイズによっては、 nkwntech によって提案されているようなreadfile()
の使用が優れています。そして、 garrow によって提案されたxsendfileを使用することも、Apacheによってサポートされている別の良いアイデアです。
header("Content-length:".filesize($filename));
header('Content-Type: application/Zip'); // Zip file
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="downloadpackage.Zip"');
header('Content-Transfer-Encoding: binary');
ob_end_clean();
readfile($filename);
exit();
私が過去にこれを行ったとき、私はこれを使用しました:
set_time_limit(0); //Set the execution time to infinite.
header('Content-Type: application/exe'); //This was for a LARGE exe (680MB) so the content type was application/exe
readfile($fileName); //readfile will stream the file.
これらの3行のコードは、ダウンロードのすべての作業を実行します readfile() は、指定されたファイル全体をクライアントにストリーミングします。ファイルのストリーミングが完了する前。
これが大きなファイルに適しているかどうかはわかりません。ユーザーがダウンロードを完了するまでダウンロードスクリプトのスレッドが実行され、Apacheのようなものを実行している場合、Apacheは長時間実行されるように設計されていないため、50以上の同時ダウンロードがサーバーをクラッシュさせる可能性があります。同時にスレッド。もちろん、Apacheスレッドが何らかの理由で終了し、ダウンロードが進行している間、ダウンロードがバッファのどこかに置かれている場合、私は間違っている可能性があります。
これは、256MBのメモリ制限があるサーバー上の200+ MBのサイズのファイルでテストされます。
header('Content-Type: application/Zip');
header("Content-Disposition: attachment; filename=\"$file_name\"");
set_time_limit(0);
$file = @fopen($filePath, "rb");
while(!feof($file)) {
print(@fread($file, 1024*8));
ob_flush();
flush();
}
私は同じ問題を抱えていましたが、セッションsession_cache_limiter( 'none');を開始する前にこれを追加することで問題が解決しました。
Readfileのphpマニュアルエントリのコメントにある次のスニペットを使用しました。
function _readfileChunked($filename, $retbytes=true) {
$chunksize = 1*(1024*1024); // how many bytes per chunk
$buffer = '';
$cnt =0;
// $handle = fopen($filename, 'rb');
$handle = fopen($filename, 'rb');
if ($handle === false) {
return false;
}
while (!feof($handle)) {
$buffer = fread($handle, $chunksize);
echo $buffer;
ob_flush();
flush();
if ($retbytes) {
$cnt += strlen($buffer);
}
}
$status = fclose($handle);
if ($retbytes && $status) {
return $cnt; // return num. bytes delivered like readfile() does.
}
return $status;
}