PHPを使用して、許可の問題のためにWebアクセス可能なディレクトリにない大きなファイル(最大200MBまで)を提供しようとしています。現在、いくつかのヘッダーとともにreadfile()
呼び出しを使用してファイルを処理していますが、PHPは送信前にメモリにロードしているようです。共有ホスティングサーバー。大量のメモリを使用したり、X-Sendfileなどの独自のApacheモジュールを追加したりすることはできません。
セキュリティ上の理由から、ファイルをWebアクセス可能なディレクトリに配置することはできません。共有ホスティングサーバーに展開できるメモリ集約的でない方法を知っている人はいますか?
編集:
if(/* My authorization here */) {
$path = "/uploads/";
$name = $row[0]; //This is a MySQL reference with the filename
$fullname = $path . $name; //Create filename
$fd = fopen($fullname, "rb");
if ($fd) {
$fsize = filesize($fullname);
$path_parts = pathinfo($fullname);
$ext = strtolower($path_parts["extension"]);
switch ($ext) {
case "pdf":
header("Content-type: application/pdf");
break;
case "Zip":
header("Content-type: application/Zip");
break;
default:
header("Content-type: application/octet-stream");
break;
}
header("Content-Disposition: attachment; filename=\"".$path_parts["basename"]."\"");
header("Content-length: $fsize");
header("Cache-control: private"); //use this to open files directly
while(!feof($fd)) {
$buffer = fread($fd, 1*(1024*1024));
echo $buffer;
ob_flush();
flush(); //These two flush commands seem to have helped with performance
}
}
else {
echo "Error opening file";
}
fclose($fd);
fopen
の代わりにfread
とreadfile
を使用すると、問題が解決するはずです。
PHPのreadfile
ドキュメントには、 解決策 があり、fread
を使用して目的を実行する方法が示されています。
サーバーから大きなファイルをダウンロードするために、php.iniファイルの以下の設定を変更しました。
Upload_max_filesize - 1500 M
Max_input_time - 1000
Memory_limit - 640M
Max_execution_time - 1800
Post_max_size - 2000 M
これで、サーバー上で175MBのビデオをアップロードおよびダウンロードできます。以来、私は専用サーバーを持っています。したがって、これらの変更を行うのは簡単でした。
以下は、ファイルをダウンロードするためのPHPスクリプトです。このコードスニペットでは、ファイルサイズが大きい場合に変更を加えていません。
// Begin writing headers
ob_clean(); // Clear any previously written headers in the output buffer
if($filetype=='application/Zip')
{
if(ini_get('zlib.output_compression'))
ini_set('zlib.output_compression', 'Off');
$fp = @fopen($filepath, 'rb');
if (strstr($_SERVER['HTTP_USER_AGENT'], "MSIE"))
{
header('Content-Type: "$content_type"');
header('Content-Disposition: attachment; filename="'.$filename.'"');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header("Content-Transfer-Encoding: binary");
header('Pragma: public');
header("Content-Length: ".filesize(trim($filepath)));
}
else
{
header('Content-Type: "$content_type"');
header('Content-Disposition: attachment; filename="'.$filename.'"');
header("Content-Transfer-Encoding: binary");
header('Expires: 0');
header('Pragma: no-cache');
header("Content-Length: ".filesize(trim($filepath)));
}
fpassthru($fp);
fclose($fp);
}
elseif($filetype=='audio'|| $filetype=='video')
{
global $mosConfig_absolute_path,$my;
ob_clean();
header("Pragma: public");
header('Expires: 0');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: pre-check=0, post-check=0, max-age=0');
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Type: application/force-download");
header("Content-Type: $content_type");
header("Content-Length: ".filesize(trim($filepath)));
header("Content-Disposition: attachment; filename=\"$filename\"");
// Force the download
header("Content-Transfer-Encoding: binary");
@readfile($filepath);
}
else{ // for all other types of files except Zip,audio/video
ob_clean();
header("Pragma: public");
header('Expires: 0');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: pre-check=0, post-check=0, max-age=0');
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Type: $content_type");
header("Content-Length: ".filesize(trim($filepath)));
header("Content-Disposition: attachment; filename=\"$filename\"");
// Force the download
header("Content-Transfer-Encoding: binary");
@readfile($filepath);
}
exit;
パフォーマンスに関心がある場合は、モジュールとしてApache、nginx、およびlighttpdで使用可能なxsendfileがあります。 readfile()
docのユーザーのコメントを確認してください。
また、これらのWebサーバー用のモジュールがあり、追加のハッシュ値を含むURLを受け入れて、短時間でファイルをダウンロードできます。これは、認証の問題を解決するためにも使用できます。
ゴーディアンノットのスタイルでこれを処理することもできます。つまり、問題を完全に回避します。ファイルをアクセスできないディレクトリに保管してください。ダウンロードが開始されると、簡単に
_tempstring = Rand();
symlink('/filestore/filename.extension', '/www/downloads'.tempstring.'-filename.extension');
echo("Your download is available here: <a href='/downloads/'.tempstring.'-filename.extension');
_
cronjobをセットアップして、unlink()
10分以上前のダウンロードリンクを設定します。データの処理やHTTPヘッダーのマッサージなどはほとんど必要ありません。
この目的のために、いくつかのライブラリもあります。