с использованием MAMP v2.0 на Mac _ _ Apache / 2.0.64 (Unix) - PHP / 5.3.5 - DAV / 2 mod_ssl / 2.0.64 - OpenSSL / 0.9.7l- MySQL 5.5.9
У меня есть скрипт, который я пытаюсь запустить, и, похоже, он дает мне серьезные утечки памяти, которые я пытался отладить и не могу решить, как исправить.
По сути, скрипт является частью модуля файлового менеджера.Он обрабатывает загрузку файла, когда ему присваивается идентификатор.
Весь файл хранится в таблице базы данных в виде большого двоичного объекта в кусках по 64 КБ (на запись) и передается клиенту по запросу.
База данных: file_management
Таблицы: 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 - дата и время
FileUploadBy - int (5)
file_details :
FileDataID - int (10) AUTO_INCREMENT
FileID - int (10)
FileData - BLOB
На самом деле я получаю ошибку (из журнала ошибок php):
[31 октября2011 09:47:39] Неустранимая ошибка PHP: допустимый объем памяти 134217728 байт исчерпан (попытался выделить 63326173 байт) в / root / htdocs / file_manager / file_manager_download.php в строке 150
Теперь фактическая функция загрузки работает, если файл достаточно мал, в данном случае менее 40 МБ, однако, если он превышает этот размер, например, файл 60 МБ вошибка выше, это не удается.Все, что он делает, это загружает файл размером 0 КБ.
Очевидно, что 134217728 байт превышает 63326173 байта (128 МБ против 60 МБ).
Допустимый объем памяти 134217728 байт - этодиректива в php.ini: «memory_limit = 128M; максимальный объем памяти, который скрипт может потреблять»
Если я установлю это значение 256M, это позволит мнезагрузить этот файл 60 МБ, а также примерно до 80 МБ файла.
Кроме того, если я установлю это значение на 1024 МБ, это позволит мне загрузить файл 260 МБ и, возможно, больше.
Таким образом, вы можетевидите, что проблема заключается в утечке где-то в скрипте, который поглощает всю память.
Вот скрипт загрузки:
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.<br>Your Query: " . $sql_GetFileDetails . "<br> Error: (" . mysql_errno() . ") " . mysql_error());
if (mysql_num_rows($result_GetFileDetails) != 1) { die ("A MySQL error has occurred.<br>Your Query: " . $sql_GetFileDetails . "<br> 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<br />Your Query: " . $sql_GetFileDataNodeIDs . "<br /> 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<br />Your Query: " . $sql_GetFileDataBlobs . "<br /> 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 и выводил результаты для пикаиспользование памяти, но, похоже, ничто не приближается к пределам, в целом пиковое использование памяти для страницы было около 900 КБ.
Так что я думаю, что это объединяет куски файлов в память и не позволяет имидти или что-топохоже, но куски файлов - это единственное, что может достичь такого объема памяти, что приведет к сбою сценария.
Я могу предоставить сценарий для загрузки файла в базу данных, чтобы вы могли проверить мой сценарий, если выкак, просто дайте мне знать
Ура за любую помощь!
Мик
* ///////// решено ///////// *
Я просто хочу сказать спасибо hafichuk, отличный ответ и решил всю мою проблему.
Проблема была в два раза.
1 - я не использовал ob_flush () внутри цикла while.Я добавил это, и это, казалось, высвободило много памяти, позволяя загружать больше, но не без ограничений.
Например, с memory_limit = 128M я мог теперь загрузить более 40 Мб, фактически я мог получитьдо около 200 МБ.Но здесь это снова не удалось.Первая проблема с памятью решена.
УРОК 1: Очистите ваши объекты!
2 - Я использовал mysql_query для получения результатов для моего SQL-запроса.Проблема в том, что он буферизует эти результаты, и это добавило к моей проблеме с ограничением памяти.
Я вместо этого использовал mysql_unbuffered_query, и теперь это работает безупречно.
Это, однако, имеет некоторые ограничения, что он блокирует вашу таблицу при чтении результатов.
УРОК 2: Не буферизируйте результаты mysql, если не требуется!(в рамках программных ограничений)
ЗАКЛЮЧИТЕЛЬНЫЙ УРОК:
Все эти исправления работают, однако требуется дополнительное тестирование, чтобы убедиться в отсутствии проблем с их комбинацией.
Кроме того, я узнал намного больше об объектах и распределении памяти php, я просто хотел бы, чтобы был способ визуальной отладки процесса немного лучше, чем предлагает xdebug.Если у кого-то есть идеи о том, как xdebug мог бы пролить свет на этот процесс, пожалуйста, дайте мне знать в комментариях.
Надеюсь, что это поможет кому-то еще в будущем.
Приветствия
Мик