Форум: Форум PHPФорум ApacheФорум Регулярные ВыраженияФорум MySQLHTML+CSS+JavaScriptФорум FlashРазное
Новые темы: 0000000
PHP 5/6. В подлиннике. Авторы: Кузнецов М.В., Симдянов И.В. MySQL на примерах. Авторы: Кузнецов М.В., Симдянов И.В. Самоучитель PHP 5 / 6 (3 издание). Авторы: Кузнецов М.В., Симдянов И.В. MySQL 5. В подлиннике. Авторы: Кузнецов М.В., Симдянов И.В. PHP 5. На примерах. Авторы: Кузнецов М.В., Симдянов И.В., Голышев С.В.
ВСЕ НАШИ КНИГИ
Консультационный центр SoftTime

Форум PHP

Выбрать другой форум

 

Здравствуйте, Посетитель!

вид форума:
Линейный форум Структурный форум

тема: Fatal error: Allowed memory size of....
 
 автор: hammet   (25.05.2007 в 13:30)   письмо автору
 
 

Долго мучался с веб интерфейсом для работы с фтп. Даю пользователю возможность скачать файлы mp3.
Файлы лежат на удаленном фтп-сервере. Скрипт соединяется с сервером читает содержимое и выдает его пользователю (pop-up открыть/сохранить), предварительно отправив необходимые хеды.
Я поставил в своем php.ini мемори сайз 16М - этого вполне хватало.

Но! Когда дело дошло до 80-минутных ДиДжейских сетов размером 130-170 Мб..... :(
Естесственно увеличивать размер выделяемой памяти в php.ini не является рациональным)

Подскажите, может эту проблему можно решить используя Output Control Functions?
Никогда ранее с буффером вывода не приходилось работать.
Спасибо.

Fatal error: Allowed memory size of 
16777216 bytes exhausted (tried to allocate 
4097 bytes) in /var/www/html/meloman/libs/FTP.class.php on 
line 999

   
 
 автор: Trianon   (25.05.2007 в 14:14)   письмо автору
 
   для: hammet   (25.05.2007 в 13:30)
 

Вы их что - в память читаете?!

   
 
 автор: hammet   (25.05.2007 в 14:16)   письмо автору
 
   для: Trianon   (25.05.2007 в 14:14)
 

ммм.....ну да :-[
Очень быстро работает :)
Есстесно если файл небольшой :)

вот кусок моего download.php


if(!$contents = $ftp->read($filename)) throw new Exception($error);
header("Content-Disposition: attachment; filename=$name");
header("Accept-Ranges: bytes");
header("Content-type: application/octet-stream");
echo $contents;

   
 
 автор: Trianon   (25.05.2007 в 14:27)   письмо автору
 
   для: hammet   (25.05.2007 в 14:16)
 

[поправлено модератором]

   
 
 автор: hammet   (25.05.2007 в 14:32)   письмо автору
 
   для: Trianon   (25.05.2007 в 14:27)
 

Какой же выход?
PS я и не смеялся

если читать в файл - немзвестно сколько придется ждать завершения работы скрипта.
К тому же на сервере включен безопаный режим. И set_time_limit(0) не сработает.
Скрипт просто умрет недокачав файл

   
 
 автор: Trianon   (25.05.2007 в 14:35)   письмо автору
 
   для: hammet   (25.05.2007 в 14:32)
 

В файл сохранять. Килобайта по 64.
64К занимает в 256 раз меньше чем 16М, а прироста скорости Вы не ощутите.
Всё равно львиную долю времени займет FTP-обмен

   
 
 автор: Trianon   (25.05.2007 в 14:32)   письмо автору
 
   для: Trianon   (25.05.2007 в 14:27)
 

Особенно, если учесть, что вы ЯВНО заявляете "Принимаю диапазонные запросы! Поддерживаю докачку!"
А потом тупо шлете весь контент.

   
 
 автор: hammet   (25.05.2007 в 14:43)   письмо автору
 
   для: Trianon   (25.05.2007 в 14:32)
 

Я не совсем понял - в файл писать по 64К а потом выдать весь файл? Или это должно происходить асинхронно?
Можете показать на примере?
Как и где мне резать контент по 64 К?

   
 
 автор: Trianon   (25.05.2007 в 15:17)   письмо автору
 
   для: hammet   (25.05.2007 в 14:43)
 

Может Вы покажете скрипт, которым файл из FTP вытягиваете?

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

   
 
 автор: hammet   (25.05.2007 в 15:30)   письмо автору
 
   для: Trianon   (25.05.2007 в 15:17)
 


<?php
...
try {
           
$ftp = new FTP();
           if(!
$ftp->connect($cfg['ftp']['host'])) throw new Exception($error);
           
$ftp->login($cfg['ftp']['user'], $cfg['ftp']['pass']);
                ...........
                if(!
$contents $ftp->read($filename)) throw new Exception($error);
           
header("Content-Disposition: attachment; filename=$name");
                
header("Accept-Ranges: bytes");
                
header("Content-type: application/octet-stream");
                echo 
$contents;
                exit();

?>


И метод read класса FTP


<?php
                        
public function read($remoteFile,$localFile=NULL)
            {
                
$fp=null;
                if(!
$this->_ready)
                {
                    
$this->_error="Please login to FTP server first.";
                    return 
false;
                }
                
preg_match("/\..+/",$remoteFile,$matches);
                
$ext=strtolower(substr($matches[0],1));
                if(
$this->_type==FTP_ASCII or ($this->_type==FTP_AUTOASCII and in_array($ext,$this->AutoAsciiExt)))
                 
$mode=FTP_ASCII;
                else
                 
$mode=FTP_BINARY;

                
$mode=FTP_BINARY;
                
$this->_mode($mode);     

                if(!
$this->_listen())
                {
                    
$this->sendMsg("Failed to listen to server.");
                     return 
false;
                }
                if(
$localFile==true)
                    
$localFile=$remoteFile;
                if(@
file_exists($localFile))
                    
$this->sendMsg("$localFile will be overwritten.");

                if(!empty(
$localFile))                    
                {
                    
$fp=fopen($localFile,"w");
                    if(!
$fp)
                    {
                        
$this->_error="Couldn't open $localFile to write.";
                        return 
false;
                    }
                }
                
                if(!
$this->_put("RETR $remoteFile"))
                {
                    
$this->_error="Failed to send command to FTP Server.";
                    @
fclose($fp);
                    
$this->_data_close();
                    return 
false;
                }
                
$msg=$this->_read();
                if(!
$this->_checkReply())
                {
                    
$this->_error=$msg;
                    @
fclose($fp);
                    
$this->_data_close();
                    return 
false;
                }
                
$this->sendMsg($msg);
                
$out=$this->_read_data($mode,$fp);

                @
fclose($fp);
                
$this->_data_close();

                
$msg=$this->_read();
                if(!
$this->_checkReply())
                {
                    
$this->_error=$msg;
                    return 
false;
                }
                
$this->sendMsg($msg);
                
$this->sendMsg("File successfuly read.");
                return 
$out;
            }
?>

   
 
 автор: Trianon   (25.05.2007 в 15:47)   письмо автору
 
   для: hammet   (25.05.2007 в 15:30)
 

толку то... он упирается в _read_data
прикрепите архивом весь класс.

   
 
 автор: hammet   (25.05.2007 в 15:38)   письмо автору
 
   для: Trianon   (25.05.2007 в 15:17)
 

Дык я вот и спрашиваю как это сделать?

   
 
 автор: hammet   (25.05.2007 в 15:56)   письмо автору
4.6 Кб
 
   для: hammet   (25.05.2007 в 15:38)
 

ok

   
 
 автор: Trianon   (25.05.2007 в 16:07)   письмо автору
 
   для: hammet   (25.05.2007 в 15:56)
 

Второй параметр метода read - имя файла, куда класть .
Всё уже реализовано в классе.

   
 
 автор: hammet   (25.05.2007 в 16:27)   письмо автору
 
   для: Trianon   (25.05.2007 в 16:07)
 

Все таже ошибка - при сохранении файла размером в 180 Мб - имеем файл размером 100 Байт с контентом Fatal
error: Allowed memory size..........
Хотя я пишу на винчестер а потом выдаю пользователю.


$tmp_file = 'tpl_cache/download_file'.md5(microtime());
       if(!$contents = $ftp->read($filename, $tmp_file)) throw new Exception($error);
       header("Content-Disposition: attachment; filename=$name");
        header("Accept-Ranges: bytes");
        header("Content-type: application/octet-stream");
        echo file_get_contents($tmp_file);
        exit(); 



хотя в кэше лежит нужный файл нужного размера и с нужным контентом. Значит он с фтп скачался быстро и вся проблема в другом.

Может хеды неправильные?

   
 
 автор: Trianon   (25.05.2007 в 17:17)   письмо автору
 
   для: hammet   (25.05.2007 в 16:27)
 

строку echo file_get_contents($tmp_file);
замените на fpassthru(fopen($tmp_file, 'rb'))

file_get_contents читает файл в память. целиком.

   
 
 автор: hammet   (25.05.2007 в 17:36)   письмо автору
 
   для: Trianon   (25.05.2007 в 17:17)
 

Спасибо большое, Trianon, Вы как всегда меня выручили :)
Все работает.
Нашел также 2 вариант загрузки по частям:



<?php
function readfile_chunked ($filename) {
               
$chunksize 1*(1024*1024);
               
$buffer '';
               
$handle fopen($filename'rb');
               if (
$handle === false)  {
                   return 
false;
               }
               while (!
feof($handle))  {
                   
$buffer fread($handle$chunksize);
                   print 
$buffer;
               }
               return 
fclose($handle);
           }
           
           
header("Content-type: application/octet-stream");
           
header("Content-Disposition: attachment; filename=$name");
           
header('Content-Length: '.filesize($tmp_file));
           
$b readfile_chunked($tmp_file);
?>


тоже работает как надо!



Так же, кому интересно, почитайте эту статью.
http://webmastak.com/article.aspx?id=322

   
 
 автор: Trianon   (25.05.2007 в 18:37)   письмо автору
 
   для: hammet   (25.05.2007 в 17:36)
 

Это не вариант загрузки по частям.
Загрузка по частям - вот:
http://softtime.ru/forum/read.php?id_forum=1&id_theme=37589

   
Rambler's Top100
вверх

Rambler's Top100 Яндекс.Метрика Яндекс цитирования