1

我是一个初学者级开发人员学习php。我需要做的任务是将包含数据的6gb CSV文件上传到数据库中。我需要访问数据,即通过controller.php文件读取文件然后拆分将巨大的 CSV 文件转换为 10,000 行输出 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);
        }
     }
}  
4

2 回答 2

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);
        }
     }
于 2018-10-09T15:41:41.507 回答
0

这是按行数(由 定义$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;

}

如果从 web 运行,您将无法在一次 php 执行中转换如此数量的数据,因为 php 脚本的最大执行时间通常在 30-60 秒之间,这是有原因的 - 不要尝试将其扩展到一些巨大的数字。如果您希望脚本运行几个小时,您需要从命令行调用它,但您也可以从另一个脚本(例如您拥有的控制器)以类似的方式调用它您可以这样做:

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

就是这样。

您拥有的控制器将无法判断整个数据是否已转换,因为在此之前它将被终止。您可以做的是编写自己的代码convert.php来更新数据库中的某些字段,并且应用程序中的其他控制器可以读取它并向用户打印 runnig 的进度convert.php

另一种方法是创建可以放入队列中的作业/作业,并且可以由作业管理器进程与可以处理转换的工作人员一起运行,但我认为这对你的需要来说太过分了。

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

于 2018-10-09T15:35:14.947 回答