[ Поиск ] - [ Пользователи ] - [ Календарь ]
Полная Версия: Отдача файлов через fread - прерывается на 5 МБ
ArtKuznetsov
Всем привет!

Возникла такая проблема: отдаю большие файлы с помощью такого кода:


$backupDir = '/home/backup';
$fileName = $_POST['fileName'];
$file = "$backupDir/$fileName";

if (file_exists($file))
{
header($_SERVER["SERVER_PROTOCOL"] . ' 200 OK');
header('Content-Type: application/zip');
header('Last-Modified: ' . gmdate('r', filemtime($file)));
header("Accept-Ranges: bytes");
header('Content-Length: ' . (filesize($file)));
header('Connection: Keep-alive');
header('Content-Disposition: attachment; filename="' . $fileName . '";');
header('Cache-Control: maxage=3600');
header('Pragma: private');

$f = fopen($file, 'r');
while(!feof($f)) {
echo fread($f, 8192);
flush();
}
fclose($f);
}


Загрузка файла прерывается на ~5 МБ. Сам файл на сервере целый, если скачать по FTP, открывается нормально. Если размер файла меньше 5 МБ, соответственно, скачивается и открывается нормально.
Пробовал внутри цикла выводить номер считываемого куска, в результате получается, что весь файл целиком нормально считывается кусками по 8КБ. То есть проблема где-то в выводе в буфер?
Заранее всем спасибо за помощь




Спустя 5 минут, 9 секунд (4.03.2011 - 10:25) Trianon написал(а):
а зачем крутить цикл, да еще и такими мелкими кусочками?
$f = fopen($file, 'rb');
fpassthru($f);

Спустя 5 минут, 24 секунды (4.03.2011 - 10:30) ArtKuznetsov написал(а):
Цитата (Trianon @ 4.03.2011 - 07:25)
а зачем крутить цикл, да еще и такими мелкими кусочками?
$f = fopen($file, 'rb');
fpassthru($f);


Спасибо за быстрый ответ,
я так понимаю, в вашем примере файл выводится целиком в память? А если у него размер гигабайт?

Спустя 9 минут, 46 секунд (4.03.2011 - 10:40) ArtKuznetsov написал(а):
Увеличил размер буфера до 1 МБ - закачка стала прерываться на 3 МБ

Спустя 1 час, 40 минут, 7 секунд (4.03.2011 - 12:20) ArtKuznetsov написал(а):
Вылечилось, если кому интересно, добавлением ob_flush():

$f = fopen($file, 'r');
while(!feof($f)) {
echo fread($f, 1024*1024);
ob_flush();
flush();
}
fclose($f);


Но скорость при этом просто ужасная, 50 Кб/сек, не больше. Это как-то связано с размером буфера?

Спустя 5 минут, 16 секунд (4.03.2011 - 12:25) Renden написал(а):
ArtKuznetsov
мб у тя интернет фиговый?))
Хз я отдаю так, проблем никаких нет

$filename = "file.xz";
if (!file_exists($filename))
{
echo "Такого файла нет";
exit;
}
$fsize = filesize($filename);
$fd = @fopen($filename, "rb");
if (!$fd){
exit;
}
else {
$content = fread($fd, filesize($filename));
fclose($fd);
header('Content-Disposition: attachment; filename="'.$filename.'"');
header("Content-Type: application/force-download");
header("Content-Type: application/download");
header("Content-Description: File Transfer");
header("Content-type: application/octet-stream");
echo $content;
exit;
}

Спустя 9 минут, 30 секунд (4.03.2011 - 12:35) ArtKuznetsov написал(а):
Renden,
Интернет нормальный. Ты отдаешь файл, предварительно полностью прочитав его в память ($content = fread($fd, filesize($filename))wink.gif, для больших файлов такое не прокатит - памяти не хватит на серваке

Спустя 5 минут, 3 секунды (4.03.2011 - 12:40) Trianon написал(а):
Цитата



>  $f = fopen($file, 'rb');
>  fpassthru($f);

я так понимаю, в вашем примере файл выводится целиком в память? А если у него размер гигабайт?


Именно что не целиком.
Вывод идет напрямую в поток соединения. То есть к браузеру.
Какой то отдельной памяти (кроме той, что требуется для обмена данными по TCP-IP соединению) не выделяется.

Спустя 14 минут, 28 секунд (4.03.2011 - 12:54) Renden написал(а):
ArtKuznetsov, я думаю что Trianon прав, так как я и 2 гб отдавал, однако у меня памяти на серваке всего гиг, да и я чето не замечал что там что-то бешено его жрет)
Хотя я может и жрет, сейчас проверю, а то вдруг сижу фигней страдаю,а у мня сервак подыхает smile.gif)

Спустя 1 час, 26 минут, 50 секунд (4.03.2011 - 14:21) ArtKuznetsov написал(а):
Цитата
Именно что не целиком.
Вывод идет напрямую в поток соединения. То есть к браузеру.
Какой то отдельной памяти (кроме той, что требуется для обмена данными по TCP-IP соединению) не выделяется.


Хоть и в документации ясно написано:
Цитата
Reads to EOF on the given file pointer from the current position and writes the results to the output buffer.

всё равно проверил. В результате при попытке скачивания она снова обрывается на 4 МБ и получаю запись в логе веб-сервера:

[Fri Mar  4 14:19:13 2011] [error] (12)Cannot allocate memory: fork: Unable to fork new process


Хостинг слабенький, 300 МБ памяти всего. Но этого все равно должно хватать, если передавать файл по кусочкам

Спустя 33 секунды (4.03.2011 - 14:22) ArtKuznetsov написал(а):
А сделал так (как на примере http://www.php.net/manual/ru/function.passthru.php#54108):

header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"$fileName\"");
header("Content-Length: " . filesize($file));
passthru("cat $file", $err);

Спустя 27 минут, 57 секунд (4.03.2011 - 14:50) ArtKuznetsov написал(а):
Не знаю, что поменялось, но скопипастив пример с http://stackoverflow.com/questions/4988681...y-limit-problem всё заработало:

$chunksize = 1 * (1024 * 1024);
if (filesize($file) > $chunksize)
{
$handle = fopen($file, 'rb');
$buffer = '';
while (!feof($handle))
{
$buffer = fread($handle, $chunksize);
echo $buffer;
ob_flush();
flush();
}
fclose($handle);
}
else
{
readfile($file);
}

Спустя 3 минуты, 57 секунд (4.03.2011 - 14:54) Renden написал(а):
ArtKuznetsov
Цитата
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"$fileName\"");
header("Content-Length: " . filesize($file));
passthru("cat $file", $err);

Ну я как понял ты вывел содержимое файла в консоль, а разница в памяти разве есть?, то его выводишь через bash а раньше выводил через php.

Теперь файлы качаются с нормальной скоростью?)

Спустя 2 минуты, 14 секунд (4.03.2011 - 14:56) ArtKuznetsov написал(а):
Цитата (Renden @ 4.03.2011 - 11:54)
ArtKuznetsov
Цитата
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"$fileName\"");
header("Content-Length: " . filesize($file));
passthru("cat $file", $err);

Ну я как понял ты вывел содержимое файла в консоль, а разница в памяти разве есть?, то его выводишь через bash а раньше выводил через php.

Теперь файлы качаются с нормальной скоростью?)

Это решение так и не заработало. Заработало то, что в последнем сообщении, через fread(). И скорость теперь отличная!

Спустя 2 минуты, 16 секунд (4.03.2011 - 14:58) Renden написал(а):
ArtKuznetsov
А ты прочитал что там написано было?
Чувак задает вопрос типа у него на сервере 32 мб памяти, и при загрузке 40 мб его скрипт уже не пашет, и спрашивает в чем трабл. Ты уверен что при твоих 300мб у тя допустим файл на 500мб скачается?

Спустя 10 минут, 11 секунд (4.03.2011 - 15:08) ArtKuznetsov написал(а):
Renden,
Прочитай плиз сам повнимательнее, чувак писал, что не работает у него:
- во-первых, через readfile,
- во-вторых, не память у сервера 32 МБ, а memory limit (параметр в php.ini)
- в-третьих, решение, которое у меня заработало, он не мог применить, потому что
Цитата
To make it even more difficult for me, they don't allow fopen...

Спустя 3 минуты, 59 секунд (4.03.2011 - 15:12) Nikitian написал(а):
Никогда. Никогда! Никогда не отдавайте файл через цикл пыха! Это не способ ограничения скорости отдачи. Для раздачи статики есть лёгкие веб-серверы. Это не работа для пыха! Нет возможности конфигурировать веб-сервер, не занимайтесь онанизмом и сделайте прямую ссылку.
Как правильно: 1 и 2

P.S. Зол, т.к. недавно осчастливил одного заказчика ускорением в разы сервера за счёт переписывания такой же быдлокодерской отдачи адалт-контента.

Спустя 47 минут, 44 секунды (4.03.2011 - 16:00) ArtKuznetsov написал(а):
Nikitian,
Спасибо, я рассматривал этот вариант. У меня на хостинге эта возможность не установлена, а дополнительно устанавливать не сильно хочется. Вообще, конечно, Lighttp правильное решение
А прямую ссылку нет возможности сделать, т.к. файлы размещаются за пределами web root

Спустя 4 минуты, 20 секунд (4.03.2011 - 16:04) Nikitian написал(а):
lighttp, nginx - не суть важно. Даже если файлы за пределами webroot, это не значит, что нельзя nginx'у указать свой webroot для определённых location wink.gif
Быстрый ответ:

 Графические смайлики |  Показывать подпись
Здесь расположена полная версия этой страницы.
Invision Power Board © 2001-2024 Invision Power Services, Inc.