Как разделить CSV-файл размером 6 ГБ на куски, используя php - PullRequest
0 голосов
/ 09 октября 2018

Я начинающий разработчик, изучающий 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 ]

0 голосов
/ 09 октября 2018

Может быть, это потому, что вы создаете новый $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);
        }
     }
0 голосов
/ 09 октября 2018

Вот рабочий пример разделения 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-файлом, который легко интерпретировать (импортировать), где в исходном файле есть только одна строка заголовка.

...