2011-10-31 16 views
3

MacでMAMP v2.0を使用_ _ Apache/2.0.64(Unix) - PHP/5.3.5 - DAV/2 mod_ssl/2.0.64 - OpenSSL/0.9.7l - MySQL 5.5.9メモリリーク、PHPおよびMySQL Blobストリーミングファイルのデバッグ

私は実行しようとしているスクリプトを持っています。私はデバッグしようとしましたが、修正する方法を解決できない大きなメモリリークを与えているようです。

基本的に、スクリプトはファイルマネージャモジュールの一部です。 IDを指定すると、ファイルのダウンロードを処理します。

ファイル全体は、BLOBとして64KBのチャンク(レコードごと)でデータベーステーブルに格納され、要求に応じてクライアントにストリーム配信されます。

Database: file_management

Tables: file_details, file_data

file_details:
FileID - int(10) AUTO_INCREMENT
FileTypeID - int(10)
FileType - varchar(60)
FileName - varchar(255)
FileDescription - varchar(255)
FileSize - bigint(20)
FileUploadDate - datetime
FileUploadBy - int(5)

file_details:
FileDataID - int(10) AUTO_INCREMENT
FileID - int(10)
FileData - BLOB

私は実際に(PHPのエラーログから)この1で取得していますエラー:

[10月31日 - 2011年9時47分39秒] PHPの致命的なエラー:134217728バイトの許可メモリサイズを/root/htdocs/file_manager/file_manager_download.phpの行150で使い果たされた(63326173バイトを割り当てようと試みました)

ファイルが十分に小さい場合、実際のダウンロード機能は40MB未満ですが、しかし、それが上になると、上記のエラーの60mbファイルのように、失敗します。それは0kbのファイルをダウンロードするだけです。

明らかに、134217728バイトは63326173バイト(128MBと60MB)を超えています。 134217728バイトの

可メモリサイズは、php.iniのディレクティブです:「のmemory_limit = 128M;メモリの最大量は、スクリプトが消費される可能性があり、」私は256Mに設定すると

を、それは私がその60mbのファイルをダウンロードすることを可能にするだけでなく、約80mbのファイルをダウンロードすることができます。

また、これを1024Mに設定すると、260MBのファイルをダウンロードすることができます。

これは、問題がすべてのメモリを浪費しているスクリプトのどこかでリークしていることがわかります。ここで

は、ダウンロード・スクリプトです:

 


    ini_set('display_errors',1); 
error_reporting(E_ALL & ~E_NOTICE); 

$strDB=mysql_connect("localhost","username","password")or die ("Error connecting to mysql.. Error: (" . mysql_errno() . ") " . mysql_error()); 
$database=mysql_select_db("file_management",$strDB); 

if (isset($_GET["id"])) { 

    // List of nodes representing each 64kb chunk 
    $nodelist = array(); 

    // Pull file meta-data 
    $sql_GetFileDetails = " 
    SELECT 
    FileID, 
    FileTypeID, 
    FileType, 
    FileName, 
    FileDescription, 
    FileSize, 
    FileUploadDate, 
    FileUploadBy 
    FROM file_details WHERE FileID = '".$_GET["id"]."';"; 

    $result_GetFileDetails = mysql_query($sql_GetFileDetails) or die ("No results for this FileID.
Your Query: " . $sql_GetFileDetails . "
Error: (" . mysql_errno() . ") " . mysql_error()); if (mysql_num_rows($result_GetFileDetails) != 1) { die ("A MySQL error has occurred.
Your Query: " . $sql_GetFileDetails . "
Error: (" . mysql_errno() . ") " . mysql_error()); } // Set the file object to get details from $FileDetailsArray = mysql_fetch_assoc($result_GetFileDetails); // Pull the list of file inodes $sql_GetFileDataNodeIDs = "SELECT FileDataID FROM file_data WHERE FileID = ".$_GET["id"]." order by FileDataID"; if (!$result_GetFileDataNodeIDs = mysql_query($sql_GetFileDataNodeIDs)) { die("Failure to retrive list of file inodes
Your Query: " . $sql_GetFileDataNodeIDs . "
Error: (" . mysql_errno() . ") " . mysql_error()); } while ($row_GetFileDataNodeIDs = mysql_fetch_assoc($result_GetFileDataNodeIDs)) { $nodelist[] = $row_GetFileDataNodeIDs["FileDataID"]; } $FileExtension = explode(".",$FileDetailsArray["FileName"]); $FileExtension = strtolower($FileExtension[1]); // Determine Content Type switch ($FileExtension) { case "mp3": $ctype="audio/mp3"; break; case "wav": $ctype="audio/wav"; break; case "pdf": $ctype="application/pdf"; break; //case "exe": $ctype="application/octet-stream"; break; case "zip": $ctype="application/zip"; break; case "doc": $ctype="application/msword"; break; case "xls": $ctype="application/vnd.ms-excel"; break; case "ppt": $ctype="application/vnd.ms-powerpoint"; break; case "gif": $ctype="application/force-download"; break; // This forces download, instead of viewing in browser. case "png": $ctype="application/force-download"; break; // This forces download, instead of viewing in browser. case "jpeg": $ctype="application/force-download"; break; // This forces download, instead of viewing in browser. case "jpg": $ctype="application/force-download"; break; // This forces download, instead of viewing in browser. default: $ctype="application/force-download"; // This forces download, instead of viewing in browser. } // Send down the header to the client header("Date: ".gmdate("D, j M Y H:i:s e", time())); header("Cache-Control: max-age=2592000"); //header("Last-Modified: ".gmdate("D, j M Y H:i:s e", $info['mtime'])); //header("Etag: ".sprintf("\"%x-%x-%x\"", $info['ino'], $info['size'], $info['mtime'])); header("Accept-Ranges: bytes"); //header("Cache-Control: Expires ".gmdate("D, j M Y H:i:s e", $info['mtime']+2592000)); header("Pragma: public"); // required header("Expires: 0"); header("Cache-Control: must-revalidate, post-check=0, pre-check=0"); header("Cache-Control: private",false); // required for certain browsers header("Content-Description: File Transfer"); header("Content-Disposition: attachment; filename=\"".$FileDetailsArray["FileName"]."\""); header("Content-Transfer-Encoding: binary"); header("Content-Type: ".$FileDetailsArray["FileSize"]); ob_end_clean(); ob_start(); ob_start("ob_gzhandler"); $sql_GetFileDataBlobs = "SELECT FileData FROM file_data WHERE FileID = ".$_GET["id"]." ORDER BY FileDataID ASC;"; if (!$result_GetFileDataBlobs = mysql_query($sql_GetFileDataBlobs)) { die("Failure to retrive list of file inodes
Your Query: " . $sql_GetFileDataBlobs . "
Error: (" . mysql_errno() . ") " . mysql_error()); } while ($row_GetFileDataBlobs = mysql_fetch_array($result_GetFileDataBlobs)) { echo $row_GetFileDataBlobs["FileData"]; } ob_end_flush(); header('Content-Length: '.ob_get_length()); ob_end_flush(); }

私はXdebugをし、出力ピークメモリ使用量の結果を使用していたが、何も総ピークメモリ使用量で、限界近くのどこかのような何かをして行くことが表示されません900kbのページ。

私はそれがメモリにファイルチャンクを集約していると思っているのですが、ファイルのチャンクがその量に達する唯一のものであり、スクリプトが失敗する原因になります。

私はあなたが好きなら、あなたが私のスクリプトをテストすることができるようにデータベースにファイルをアップロードするスクリプトを提供することができ、ちょうど私は任意の助け

乾杯を教えて!

ミック


* ///////// ///////// *

を解決した私は、hafichukする感謝を言うために偉大な応答をしたいです私の全問題を解決しました。

問題は2倍でした。

1 - 私はwhileループ内でob_flush()を使用していませんでした。私はそれを追加し、メモリを解放してダウンロード数を増やすことができましたが、無制限ではないことが分かりました。

例えば、memory_limit = 128Mとすると、今では40MB以上をダウンロードできるようになりました。実際には約200MBまで増やすことができました。しかし、これが再び失敗した場所です。最初のメモリ問題が解決しました。

レッスン1:オブジェクトをフラッシュしてください!

2 - 私は私のSQLクエリの結果を取得するためにmysql_queryを使用していました。問題は、これらの結果をバッファリングし、これが私のメモリ制限の問題に追加されていることです。

代わりにmysql_unbuffered_queryを使用しましたが、これは完全に機能します。

これにはいくつかの制限がありますが、結果を読み取っている間にテーブルがロックされます。

レッスン2:必要ない場合は、mysql結果をバッファしないでください。

(プログラム的な制限の範囲内で)最後のレッスン:

これらの修正作業のすべてが、しかし、それはそれらの組み合わせに問題がないことを確認するためにいくつかのより多くのテストが必要です。

また、私はオブジェクトとPHPのメモリ割り当てについて多くのことを学びました。xdebugが提供するものよりも少し視覚的にプロセスを視覚的にデバッグする方法があればいいと思います。誰かがxdebugがこのプロセスで実際にどのように光を当てるかについてのアイデアがあれば、コメントに私に教えてください。

これは今後誰かを助けてくれることを願っています。

乾杯

ミック

+0

whileループでhttp://php.net/manual/en/function.ob-flush.phpを試しましたか? – hafichuk

+0

あなたは伝説です!それは(ほぼ)完全に機能します。ちょうどあなたに知らせるために、memory_limitディレクティブの完全な余裕に働くように見えるので、128Mに設定して140mbのファイルを試しても動作しますが、250mbのファイルをダウンロードしようとすると、 PHPの致命的なエラー:/ root/htdocs/file_manager/file_manager_downloadに134217728バイトの空きメモリサイズ(132392961バイトを割り当てようとしました)が使用可能になりました。[31-Oct-2011 11:46:01] 125行目の.phpはob_flush()です。ライン。 – Quantico773

+0

mysql_unbuffered_queryは私の一日を保存しました。 – webtweakers

答えて

1

は、あなたは自分のwhileループで "()ob_flush" を行う必要があるはずです。これにより、ページへのバッファがクリアされます。データの開始後にヘッダーを送信することはできないため、コンテンツの長さを示す最後のヘッダーは削除する必要があります。ダウンロードの進行状況を更新するだけで、ファイルをダウンロードしても問題ありません。

+0

こんにちはhafichuk、私はwhileループにこの行を追加し、私が上記のコメントで答えたように、それは動作するように見えますが、memory_limitディレクティブの限界までです。 500MBと言うことができるファイルをダウンロードする場合、php.iniのmemory_limit指令を増やす必要がありますか? ob_flushはディレクティブに関係なくファイルサイズを処理できますが、私のコードは別の場所で間違っていますか?ご協力いただき誠にありがとうございます。 – Quantico773

+0

mysql_fetch_Xはデータをバッファしますので、http://www.php.net/manual/en/function.mysql-unbuffered-query.phpを参照する必要があります。 – hafichuk

+0

データベースの外部にファイルを保存し、http://www.php.net/manual/en/function.fpassthru.phpを使用することをお勧めします。 – hafichuk

関連する問題