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

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

1

Решение

Может быть, это потому, что вы создаете новый $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);
}
}
1

Другие решения

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

0

По вопросам рекламы [email protected]