Ошибка сервера при загрузке больших файлов с помощью PHP

Я использую SFTP-функции PHPSecLib для загрузки файлов с FTP-сервера.

Линия

$sftp->get($fname); 

работает, если файл до 200 МБ, но если он 300 МБ, браузер отвечает «Firefox не может найти файл в [download.php]». То есть, он говорит, что не может найти файл php, который я использую для загрузки удаленного файла.

Сначала я думал, что это связано с установкой memory_limit в php.ini, но не имеет значения, установлено ли значение 128M или 350M; Файлы на 200 МБ все еще работают, и файлы 300 Мбайт терпят неудачу. И он проваливается через десять секунд, поэтому max_execution_time или max_input_time , похоже, не являются виновниками. Что может быть неправильным?

Related of "Ошибка сервера при загрузке больших файлов с помощью PHP"

Во-первых, я настоятельно рекомендую вам ввести код set_time_limit(0); на самом верху вашего php-файла (даже до того, как он включается), так как вы имеете дело с операциями, вы не знаете, сколько времени они предпримут.

Я бы сказал, что это случай выхода веб-сервера / браузера, когда вы не отправляли / не получали какие-либо данные за «длительный» период времени. Чтобы исправить это, нам нужно немного изменить файл SFTP.php, а именно класс Net_SFTP, затем перейти к методу get (строка 1482, если у вас есть phpseclib 0.3.1) и добавить некоторые вещи внутри единственной структуры управления «while» (я введу всю функцию ниже), чтобы добавить следующий код:

 if (strtolower(PHP_SAPI) != 'cli') { // run this if request is handled by a webserver (like your case) $my_iter++; if($my_iter > 1024){ $my_iter = 0; // reset the counter echo "transferring ... " . date("G:i:s") . "<br />"; // send something to the buffer } // flush the buffers and prevent the timeout by actually outputting something to the browser ob_end_flush(); ob_flush(); flush(); ob_start(); usleep(100); // just in case, try removing this delay } 

Который в основном выводит что-то время от времени (1024 итерации этого времени) и очищает буферы, чтобы фактически выводить что-то в браузер. Не стесняйтесь изменять значения. Из-за этих проблем это код (класс SFTP), который не должен запускаться с веб-сервера. Я имею в виду, вы МОЖЕТЕ, но вы столкнетесь с некоторыми проблемами, как этот.

Кроме того, если вы попытаетесь отправить (), вам придется сделать аналогичную модификацию по соответствующему методу, но, надеюсь, это покроет вашу проблему (по крайней мере, это устранило мои проблемы с таймаутами здесь, в моем локальном блоке dev).

Теперь полная модификация метода идет ниже, как и было обещано 😉

 function get($remote_file, $local_file = false) { if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { return false; } $remote_file = $this->_realpath($remote_file); if ($remote_file === false) { return false; } $packet = pack('Na*N2', strlen($remote_file), $remote_file, NET_SFTP_OPEN_READ, 0); if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) { return false; } $response = $this->_get_sftp_packet(); switch ($this->packet_type) { case NET_SFTP_HANDLE: $handle = substr($response, 4); break; case NET_SFTP_STATUS: // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED $this->_logError($response); return false; default: user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS', E_USER_NOTICE); return false; } if ($local_file !== false) { $fp = fopen($local_file, 'wb'); if (!$fp) { return false; } } else { $content = ''; } $read = 0; while (true) { if (strtolower(PHP_SAPI) != 'cli') { // run this if request is handled by a webserver (like your case) $my_iter++; if($my_iter > 1024){ $my_iter = 0; // reset the counter echo "transferring ... " . date("G:i:s") . "<br />"; // send something to the buffer } // flush the buffers and prevent the timeout by actually outputting something to the browser ob_end_flush(); ob_flush(); flush(); ob_start(); usleep(100); // just in case, try removing this delay } $packet = pack('Na*N3', strlen($handle), $handle, 0, $read, 1 << 20); if (!$this->_send_sftp_packet(NET_SFTP_READ, $packet)) { if ($local_file !== false) { fclose($fp); } return false; } $response = $this->_get_sftp_packet(); switch ($this->packet_type) { case NET_SFTP_DATA: $temp = substr($response, 4); $read+= strlen($temp); if ($local_file === false) { $content.= $temp; } else { fputs($fp, $temp); } break; case NET_SFTP_STATUS: $this->_logError($response); break 2; default: user_error('Expected SSH_FXP_DATA or SSH_FXP_STATUS', E_USER_NOTICE); if ($local_file !== false) { fclose($fp); } return false; } } if ($local_file !== false) { fclose($fp); } if (!$this->_send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle))) { return false; } $response = $this->_get_sftp_packet(); if ($this->packet_type != NET_SFTP_STATUS) { user_error('Expected SSH_FXP_STATUS', E_USER_NOTICE); return false; } $this->_logError($response); // check the status from the NET_SFTP_STATUS case in the above switch after the file has been closed if ($status != NET_SFTP_STATUS_OK) { return false; } if (isset($content)) { return $content; } return true; } 

Что-то еще вы могли бы сделать …

 <?php include('Net/SFTP.php'); $sftp = new Net_SFTP('www.domain.tld'); $sftp->login('username', 'password'); $start = 0; while (true) { $response = $sftp->get('1mb', false, $start, 1024); $start+= 1024; if (empty($response)) { break; } echo $response; } в <?php include('Net/SFTP.php'); $sftp = new Net_SFTP('www.domain.tld'); $sftp->login('username', 'password'); $start = 0; while (true) { $response = $sftp->get('1mb', false, $start, 1024); $start+= 1024; if (empty($response)) { break; } echo $response; } 

то есть. загрузите файл в несколько кусков.