13

When using readfile() -- using PHP on Apache -- is the file immediately read into Apache's output buffer and the PHP script execution completed, or does the PHP script execution wait until the client finishes downloading the file (or the server times out, whichever happens first)?

The longer back-story:

I have a website with lots of large mp3 files (sermons for a local church). Not all files in the audio archive are allowed to be downloaded, so the /sermon/{filename}.mp3 path is rewritten to really execute /sermon.php?filename={filename} and if the file is allowed to be downloaded then the content type is set to "audio/mpeg" and the file streamed out using readfile(). I've been getting complaints (almost exclusively from iPhone users who are streaming the downloads over 3G) that the files don't fully download, or that they cut off after about 10 or 15 minutes. When I switched from streaming out the file with a readfile() to simply redirecting to the file -- header("Location: $file_url"); -- all of the complaints went away (I even checked with a few users who could reliably reproduce the problem on demand previously).

This leads me to suspect that when using readfile() the PHP script engine is in use until the file is fully downloaded but I cannot find any references which confirm or deny this theory. I'll admit I'm more at home in the ASP.NET world and the dotNet equivalent of readfile() pushes the whole file to the IIS output buffer immediately so the ASP.NET execution pipeline can complete independently of the delivery of the file to the end client... is there an equivalent to this behavior with PHP+Apache?

4

7 に答える 7

9

あなたができるいくつかのこと(私はあなたが送信する必要のあるすべてのヘッダーを報告していませんが、それはおそらくあなたが現在あなたのスクリプトに持っているものと同じです):

set_time_limit(0);  //as already mention
readfile($filename);
exit(0);

また

passthru('/bin/cat '.$filename);
exit(0);

また

//When you enable mod_xsendfile in Apache
header("X-Sendfile: $filename");

また

//mainly to use for remove files
$handle = fopen($filename, "rb");
echo stream_get_contents($handle);
fclose($handle);

また

$handle = fopen($filename, "rb");
while (!feof($handle)){
    //I would suggest to do some checking
    //to see if the user is still downloading or if they closed the connection
    echo fread($handle, 8192);
}
fclose($handle);
于 2012-10-19T13:52:01.890 に答える
9

readfile() を実行している間、PHP 出力バッファリングがアクティブなままになっている可能性があります。それを確認してください:

if (ob_get_level()) ob_end_clean();

また

while (ob_get_level()) ob_end_clean();

このようにして、残りの出力バッファのみが apache の出力バッファになります。Apache の調整については、 SendBufferSizeを参照してください。

編集

また、 mod_xsendfile ( PHP + apache + x-sendfile などの使用法に関する SO 投稿) を見ることもできます。これにより、Web サーバーに対して、セキュリティ チェックが完了し、ファイルを配信できるようになったことを伝えることができます。

于 2012-08-03T11:59:09.207 に答える
6

スクリプトは、ユーザーがファイルのダウンロードを完了するまで実行されます。最も簡単で、最も効率的で確実に機能する解決策は、ユーザーをリダイレクトすることです。

header("Location: /real/path/to/file");
exit;

ただし、これにより、ファイルの場所が明らかになる場合があります。とにかく誰もがダウンロードできないファイルを .htaccess ファイルでパスワード保護するのは良い考えですが、おそらくデータベースを使用してアクセスを決定するので、これはオプションではありません。

別の可能な解決策は、PHP の最大実行時間を 0 に設定することです。これにより、制限が無効になります。

set_time_limit(0);

ただし、ホストはこれを許可しない場合があります。また、PHP はファイルを最初にメモリに読み込み、次に Apache の出力バッファを経由して、最後にネットワークに送信します。ユーザーにファイルを直接ダウンロードさせる方がはるかに効率的であり、最大実行時間などの PHP の制限はありません。

編集: iPhone ユーザーからこの苦情が多く寄せられる理由は、おそらく接続速度が遅いためです (3G など)。

于 2012-08-02T22:33:41.640 に答える
3

PHPを介してファイルをダウンロードするのはあまり効率的ではありません。リダイレクトを使用するのが最善の方法です。ファイルの場所を公開したくない場合、またはファイルがパブリックな場所にない場合は、内部リダイレクトを調べてください。これについて少し説明している投稿があります。PHPから内部リダイレクトを行うように Apache に指示できますか?

于 2012-08-02T22:32:48.087 に答える
3

代わりに stream_copy_to_stream() を使用してみてください。readfile() よりも問題が少ないことがわかりました。

set_time_limit(0);
$stdout = fopen('php://output', 'w');
$bfname = basename($fname);

header("Content-type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"$bfname\"");

$filein = fopen($fname, 'r');
stream_copy_to_stream($filein, $stdout);

fclose($filein);
fclose($stdout);
于 2017-02-21T16:29:34.287 に答える
0

Apache の下には、php をまったく使用しない優れたエレガントなソリューションがあります。

.htaccess 構成ファイルを、次の内容でダウンロード用に提供されるファイルを含むフォルダーに配置するだけです。

<Files *.*>
ForceType applicaton/octet-stream
</Files>

これは、ブラウザに直接表示するのではなく、このフォルダ (およびそのすべてのサブフォルダ) 内のすべてのファイルをダウンロード用に提供するように Apache に指示します。

于 2016-04-22T11:53:30.973 に答える
-1

下記URL参照

http://php.net/manual/en/function.readfile.php

<?php
$file = 'monkey.gif';

if (file_exists($file)) {
    header('Content-Description: File Transfer');
    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename='.basename($file));
    header('Content-Transfer-Encoding: binary');
    header('Expires: 0');
    header('Cache-Control: must-revalidate');
    header('Pragma: public');
    header('Content-Length: ' . filesize($file));
    ob_clean();
    flush();
    readfile($file);
    exit;
}
?>
于 2012-08-02T23:15:36.740 に答える