Как разделить CSV-файл размером 6 ГБ на куски, используя php

Я начинающий разработчик, изучающий php. Задача, которую мне нужно сделать, это загрузить CSV-файл объемом 6 ГБ, содержащий данные, в базу данных. Мне нужно получить доступ к данным, т.е. прочитать файл через файл controller.php и затем разбить его на части. этот огромный CSV-файл в 10000 строк выводит CSV-файлы и записывает данные в эти выходные CSV-файлы. Я выполнил это задание уже неделю и пока не разбираюсь. Не могли бы вы, ребята, помочь мне в решении этого вопроса.

<?php

namespace App\Http\Controllers;
use Illuminate\Queue\SerializesModels;

use App\User;
use DateTime;
use Illuminate\Http\Request;
use Storage;
use Validator;
use GuzzleHttp\Client;
use GuzzleHttp\RequestOptions;
use Queue;
use App\model;


class Name extends Controller
{


     public function Post(Request $request)
     {

         if($request->hasfile('upload')){
            ini_set('auto_detect_line_endings', TRUE);
                $main_input = $request->file('upload');
                $main_output = 'output';
                $filesize = 10000;
                $input = fopen($main_input,'r');
                $rowcount = 0;
                $filecount = 1;
                $output = '';

                // echo "here1";
                while(!feof($input)){
                    if(($rowcount % $filesize) == 0){
                        if($rowcount>0) { 
                            fclose($output);
                        }
                    $output = fopen(storage_path(). "/tmp/".$main_output.$filecount++ . '.csv','w');
                    }
                    $data = fgetcsv($input);
                    print_r($data);

                    if($data) {

                        fputcsv($output, $data);
                    }

                    $rowcount++;
                }
                fclose($output);
        }
     }
}  

2 ответа

Решение

Может быть, это потому, что вы создаете новый $output обработчик файла для каждого iteration,

Я внес некоторые коррективы, так что мы создаем файл только когда rowCount = 0 и закрываем его, когда fileSize достигнуто Так же rowCount должен быть сброшен в 0 каждый раз, когда мы закрываем файл.

public function Post(Request $request)
     {

         if($request->hasfile('upload')){
            ini_set('auto_detect_line_endings', TRUE);
                $main_input = $request->file('upload');
                $main_output = 'output';
                $filesize = 10000;
                $input = fopen($main_input,'r');
                $rowcount = 0;
                $filecount = 1;
                $output = '';

                // echo "here1";
                while(!feof($input)){
                    if ($rowCount == 0) {
                        $output = fopen('php://output', storage_path(). "/tmp/".$main_output.$filecount++ . '.csv','w');
                    }
                    if(($rowcount % $filesize) == 0){
                        if($rowcount>0) { 
                            fclose($output);
                            $rowCount = 0;
                            continue;
                        }

                    }
                    $data = fgetcsv($input);
                    print_r($data);

                    if($data) {

                        fputcsv($output, $data);
                    }

                    $rowcount++;
                }
                fclose($output);
        }
     }

Вот рабочий пример разделения файла CSV по количеству строк (определяется как$numberOfLines). Просто установите свой путь в $filePath и запустите скрипт в оболочке, например:

php -f convert.php

код скрипта:convert.php

<?php

$filePath = 'data.csv';
$numberOfLines = 10000;

$file = new SplFileObject($filePath);

//get header of the csv
$header = $file->fgets();

$outputBuffer = '';
$outputFileNamePrefix = 'datasplit-';

$readLinesCount = 1;
$readlLinesTotalCount = 1;
$suffix=0;

$outputBuffer .= $header;

while ($currentLine = $file->fgets()) {
    $outputBuffer .= $currentLine;
    $readLinesCount++;
    $readlLinesTotalCount++;

    if ($readLinesCount >= $numberOfLines) {
        $outputFilename = $outputFileNamePrefix . $suffix . '.csv';
        file_put_contents($outputFilename, $outputBuffer);
        echo 'Wrote '  . $readLinesCount . ' lines to: ' . $outputFilename . PHP_EOL;    

        $outputBuffer = $header;
        $readLinesCount = 0;
        $suffix++;
    }
}

//write remainings of output buffer if it is not empty
if ($outputBuffer !== $header) {
    $outputFilename = $outputFileNamePrefix . $suffix . '.csv';
    file_put_contents($outputFilename, $outputBuffer);
    echo 'Wrote (last time)'  . $readLinesCount . ' lines to: ' . $outputFilename . PHP_EOL;

    $outputBuffer = '';
    $readLinesCount = 0;

}

вы не сможете преобразовать такой объем данных за один запуск php, если он запускается из веб-формы, поскольку максимальное время выполнения сценариев php обычно составляет 30-60 секунд, и для этого есть причина - не пытайтесь событие расширить его до некоторого огромного числа. Если вы хотите, чтобы ваш скрипт работал даже в течение нескольких часов, вам нужно вызвать его из командной строки, но вы также можете вызвать его аналогичным образом из другого скрипта (например, у вашего контроллера). Вы делаете это следующим образом:

exec('php -f convert.php');

и это все.

Контроллер, который у вас есть, не сможет определить, были ли преобразованы все данные, потому что до того, как это произойдет, они будут прерваны. Что вы можете сделать, это написать свой собственный код в convert.php который обновляет какое-то поле в базе данных, а другой контроллер в вашем приложении может прочитать это и распечатать пользователю ход выполнения convert.php,

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

Имейте в виду, что если вы что-то разделяете и в другом месте присоединения, у вас могут возникнуть проблемы с получением чего-то неправильного в этом процессе, метод, который обеспечит вам успешное разделение, передачу и соединение ваших данных, - это вычисление HASH, т.е. SHA-1 Весь файл 6 ГБ перед разделением, отправьте этот HASH в место назначения, где необходимо объединить все небольшие части данных, объедините их в один файл 6 ГБ, вычислите HASH этого файла и сравните с тем, который был отправлен. Имейте в виду, что каждая из небольших частей ваших данных после разделения имеет свой собственный заголовок, который будет CSV-файлом, который легко интерпретировать (импортировать), где в исходном файле у вас есть только одна строка заголовка.

Другие вопросы по тегам