Чтение очень больших файлов в PHP - PullRequest
24 голосов
/ 02 октября 2008

fopen не удается, когда я пытаюсь прочитать файл очень среднего размера в PHP. A 6 meg file делает это удушающим, хотя меньшие файлы вокруг 100k просто в порядке. Я читал, что иногда необходимо перекомпилировать PHP с флагом -D_FILE_OFFSET_BITS=64, чтобы прочитать файлы более 20 гигов или что-то нелепое, но разве у меня не должно быть проблем с файлом 6 Мб? В конце концов мы захотим прочитать файлы размером около 100 мегабайт, и было бы неплохо иметь возможность открывать их, а затем читать их построчно с помощью fgets, как я могу это делать с файлами меньшего размера.

Каковы ваши хитрости / решения для чтения и выполнения операций с очень большими файлами в PHP?

Обновление: вот пример простого кодового блока, который не работает на моем 6-мегабайтном файле - PHP, похоже, не выдает ошибку, он просто возвращает false. Может быть, я делаю что-то чрезвычайно глупое?

$rawfile = "mediumfile.csv";

if($file = fopen($rawfile, "r")){  
  fclose($file);
} else {
  echo "fail!";
}

Еще одно обновление: спасибо всем за помощь, оказалось, что это что-то невероятно глупое - проблема с разрешениями. Мой маленький файл необъяснимо имел права на чтение, а больший - нет. Doh!

Ответы [ 8 ]

45 голосов
/ 02 октября 2008

Вы уверены, что сбой fopen, а не настройка времени ожидания вашего скрипта? Значение по умолчанию обычно составляет около 30 секунд или около того, и если для считывания вашего файла требуется больше времени, это может привести к отключению.

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

Если ни одна из вышеперечисленных проблем не является вашей, вы можете использовать fgets для построчного чтения файла, обрабатывая его по ходу.

$handle = fopen("/tmp/uploadfile.txt", "r") or die("Couldn't get handle");
if ($handle) {
    while (!feof($handle)) {
        $buffer = fgets($handle, 4096);
        // Process buffer here..
    }
    fclose($handle);
}

Редактировать

PHP не выдает ошибку, он просто возвращает false.

Правильно ли указан путь к $rawfile относительно места выполнения скрипта? Возможно, попробуйте установить абсолютный путь для имени файла.

7 голосов
/ 07 октября 2015

Провел 2 теста с файлом 1,3 ГБ и файлом 9,5 ГБ.

1,3 ГБ

Использование fopen()

Этот процесс использовал 15555 мс для своих вычислений.

В системных вызовах он потратил 169 мс.

Использование file()

Этот процесс использовал 6983 мс для своих вычислений.

На системные вызовы затрачено 4469 мсек.

9,5 ГБ

Использование fopen()

Этот процесс использовал 113559 мс для своих вычислений.

Он провел 2532 мс в системных вызовах.

Использование file()

Этот процесс использовал 8221 мс для своих вычислений.

На системные вызовы затрачено 7998 мс.

Кажется file() быстрее.

1 голос
/ 15 февраля 2019

• Функция fgets() работает до тех пор, пока объем текстовых файлов не превысит 20 МБ, а скорость синтаксического анализа значительно снизится.

• Функция file_ get_contents() дает хорошие результаты до 40 МБ и приемлемые результаты до 100 МБ, но file_get_contents() загружает весь файл в память , поэтому он не масштабируется.

• Функция file() губительна для больших текстовых файлов, поскольку эта функция создает массив, содержащий каждую строку текста, поэтому этот массив сохраняется в памяти, а используемая память еще больше.
На самом деле, файл размером 200 МБ, который мне удалось обработать только с memory_limit, установленным на 2 ГБ, что было неприемлемо для файлов размером более 1 ГБ, которые я собирался проанализировать.

Когда вам нужно проанализировать файлы размером более 1 ГБ, а время анализа превысило 15 секунд, и вы хотите избежать загрузки всего файла в память, вам нужно найти другой способ.

Мое решение состояло в том, чтобы проанализировать данные в произвольных маленьких кусках . Код:

$filesize = get_file_size($file);
$fp = @fopen($file, "r");
$chunk_size = (1<<24); // 16MB arbitrary
$position = 0;

// if handle $fp to file was created, go ahead
if ($fp) {
   while(!feof($fp)){
      // move pointer to $position in file
      fseek($fp, $position);

      // take a slice of $chunk_size bytes
      $chunk = fread($fp,$chunk_size);

      // searching the end of last full text line
      $last_lf_pos = strrpos($chunk, "\n");

      // $buffer will contain full lines of text
      // starting from $position to $last_lf_pos
      $buffer = mb_substr($chunk,0,$last_lf_pos);

      ////////////////////////////////////////////////////
      //// ... DO SOMETHING WITH THIS BUFFER HERE ... ////
      ////////////////////////////////////////////////////

      // Move $position
      $position += $last_lf_pos;

      // if remaining is less than $chunk_size, make $chunk_size equal remaining
      if(($position+$chunk_size) > $filesize) $chunk_size = $filesize-$position;
      $buffer = NULL;
   }
   fclose($fp);
}

Используется только память $chunk_size, а скорость немного меньше, чем у file_ get_contents(). Я думаю, что PHP Group должна использовать мой подход для оптимизации своих функций синтаксического анализа.

*) Найти функцию get_file_size() здесь .

1 голос
/ 02 октября 2008

Я использовал fopen для открытия видеофайлов для потоковой передачи, используя php-скрипт в качестве сервера потоковой передачи видео, и у меня не было проблем с файлами размером более 50/60 МБ.

1 голос
/ 02 октября 2008

Ну, вы можете попробовать использовать функцию readfile, если вы просто хотите вывести файл.

Если это не так - возможно, вам следует подумать о дизайне приложения, почему вы хотите открывать такие большие файлы по веб-запросам?

0 голосов
/ 04 августа 2014

для меня fopen() был очень медленным с файлами более 1 МБ, file() намного быстрее.

Просто пытаясь читать строки 100 за раз и создавать пакетные вставки, fopen() занимает 37 секунд, а file() - 4 секунды. Должно быть, шаг string->array встроен в file()

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

0 голосов
/ 02 октября 2008

Если проблема вызвана превышением лимита памяти, вы можете попробовать установить более высокое значение (это может работать или нет в зависимости от конфигурации php).

устанавливает ограничение памяти на 12 МБ

ini\_set("memory_limit","12M");
0 голосов
/ 02 октября 2008
...