Используя fgetcsv
, могу ли я каким-то образом сделать деструктивное чтение, где строки, которые я прочитал и обработал, будут отброшены, поэтому, если я не пройду через весь файл в первый проход, я могу вернуться и забрать, где я остановился раньше the script timed out
?
Дополнительные детали:
Я получаю ежедневный фид продуктов от поставщика, который встречается как файл размером 200 МБ. Когда я распаковываю файл, он превращается в 1.5gb .csv с почти 500 000 строк и 20-25 полей. Мне нужно прочитать эту информацию в MySQL db, в идеале с PHP, поэтому я могу запланировать CRON для запуска скрипта на моем веб-хостинге каждый день.
У меня есть жесткий тайм-аут на сервере, установленный на 180 секунд хостинг-провайдером, и максимальный максимальный объем использования памяти 128 МБ для любого отдельного скрипта. Эти ограничения меня не могут изменить.
Моя идея состояла в том, чтобы захватить информацию из .csv, используя функцию fgetcsv, но я ожидаю, что вам придется пройти несколько проходов в файле из-за 3-минутного таймаута, я думал, что было бы неплохо уничтожить файл поскольку я обрабатываю его, поэтому мне не нужно будет проводить циклы, пропускающие строки, которые уже обрабатывались в предыдущем проходе.
Из вашего описания проблемы действительно звучит так, как будто вам нужно переключать хосты. Обработка файла объемом 2 ГБ с жестким временным ограничением не является очень конструктивной средой. Сказав, что удаление строк чтения из файла еще менее конструктивно, так как вам придется переписать весь 2 ГБ на диск за вычетом той части, которую вы уже прочитали, что невероятно дорого.
Предполагая, что вы сохраните сколько строк, которые вы уже обработали, вы можете пропустить строки следующим образом:
$alreadyProcessed = 42; // for example $i = 0; while ($row = fgetcsv($fileHandle)) { if ($i++ < $alreadyProcessed) { continue; } ... }
Однако это означает, что вы читаете весь файл объемом 2 ГБ с самого начала каждый раз, когда вы проходите через него, что само по себе уже занимает некоторое время, и вы сможете обрабатывать все меньше и меньше строк при каждом запуске.
Лучшее решение здесь – запомнить текущую позицию указателя файла , для которой ftell
– это функция, которую вы ищете:
$lastPosition = file_get_contents('last_position.txt'); $fh = fopen('my.csv', 'r'); fseek($fh, $lastPosition); while ($row = fgetcsv($fh)) { ... file_put_contents('last_position.txt', ftell($fh)); }
Это позволяет вам вернуться назад к последней позиции, на которой вы были, и продолжить чтение. Вы, очевидно, хотите добавить здесь много ошибок, поэтому вы никогда не находитесь в противоречивом состоянии, независимо от того, в какой момент ваш скрипт прерывается.
Вы можете избежать ошибок таймаута и памяти в некоторой степени, когда читаете как поток. По чтению строки за строкой, а затем вставляет каждую строку в базу данных (или процесс соответственно). Таким образом, на каждой итерации сохраняется только одна строка в памяти. Обратите внимание: не пытайтесь загрузить огромный массив csv в массив, который действительно потребляет много памяти.
if(($handle = fopen("yourHugeCSV.csv", 'r')) !== false) { // Get the first row (Header) $header = fgetcsv($handle); // loop through the file line-by-line while(($data = fgetcsv($handle)) !== false) { // Process Your Data unset($data); } fclose($handle); }
сif(($handle = fopen("yourHugeCSV.csv", 'r')) !== false) { // Get the first row (Header) $header = fgetcsv($handle); // loop through the file line-by-line while(($data = fgetcsv($handle)) !== false) { // Process Your Data unset($data); } fclose($handle); }
Я думаю, что лучшее решение (это будет феноменально неэффективно для непрерывной перемотки назад и записи в открытый поток файлов) – это отслеживать положение файла каждой записи (используя ftell ) и хранить ее с данными, которые вы прочитали, – тогда, если вы должны возобновиться, а затем просто перейти к последней позиции.
Вы можете попробовать загрузить файл напрямую с помощью функции read file mysql (что, вероятно, будет намного быстрее), хотя у меня были проблемы с этим в прошлом и в итоге я написал свой собственный PHP-код.
У меня есть жесткий тайм-аут на сервере, установленный на 180 секунд хостинг-провайдером, и максимальный максимальный объем использования памяти 128 МБ для любого отдельного скрипта. Эти ограничения меня не могут изменить.
Что вы пробовали?
Память может быть ограничена другими средствами, чем файл php.ini, но я не могу себе представить, как кто-то может помешать вам использовать другое время выполнения (даже если ini_set отключен, из командной строки вы можете запустить php -d max_execution_time = 3000 /your/script.php или php -c / path / to / custom / inifile /your/script.php)
Если вы не пытаетесь вставить весь файл данных в память, тогда не должно быть проблем с ограничением памяти 128 МБ