如何使用 php 将 6 GB CSV 文件拆分成块

How do i split a 6 gb CSV file into chunks using php

我是初级开发人员学习 php.The 我需要做的任务是上传一个包含数据的 6gb CSV 文件,将其放入数据 base.I 需要访问数据,即读取通过 controller.php 文件,然后将该巨大的 CSV 文件拆分为 10,000 行输出 CSV 文件,并将数据写入这些输出 CSV 文件。我已经完成了这个任务一个星期了,我还是想通了 yet.Would 你们帮我解决这个问题。

<?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);
        }
     }
}  

这里是按行数(由 $numberOfLines 定义)拆分 CSV 文件的工作示例。只需在 $filePath 中设置路径,在 shell 中设置脚本 运行 例如:

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 中编写你自己的代码来更新数据库中的某些字段,你的应用程序中的其他控制器可以读取它并向用户打印 运行nig [=15= 的进度].

另一种方法是创建 job/jobs,您可以将其放入 queue 中,并且可以由作业管理器流程 运行 与可以负责转换的工作人员一起进行,但我认为这对您的需求来说太过分了。

请记住,如果您拆分某些内容并在不同的位置加入,您可能会遇到在该过程中出错的问题,可以确保您成功拆分、传输、合并数据的方法是计算 HASH,即拆分前整个6GB文件的SHA-1,将该HASH发送到需要合并所有小部分数据的目的地,将它们组合成一个6GB文件,计算该文件的HASH并与发送的文件进行比较。请记住,拆分后数据的每一小部分都有自己的 header 以便于解释(导入)的 CSV 文件,而在原始文件中你只有一个 header 行。

可能是因为您正在为每个 iteration.

创建一个新的 $output 文件处理程序

我做了一些调整,以便我们只在 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);
        }
     }