0

我正在编写一个 Web 应用程序来解析 Excel 文件,每个文件都包含大量数据(约 47 列和数千行)。框架是 Laravel 4.2,使用的包是 laravel-excel (maatwebsite/excel)。

当我编写所​​有代码时,我有一个来自客户的示例文件,它包含 620 行并且一切正常。现在,一些文件可以工作,而大多数文件都不能。错误很奇怪。好的,一步一步来:

逻辑

用户通过 选择一个文件<input type="file">,然后这个文件通过 发送到服务器$.ajax,服务器用这个文件的属性创建一个Job实例并将这个实例返回给客户端。客户端收到此Job实例并查看progress此实例Job即解析的行数)是否低于total即总行数)。如果是这样,则客户端向服务器发送一个请求以执行此操作Job即解析更多行,例如 200)。所以客户端和服务器之间总是有对话,像这样:

  • 客户:尊敬的服务器,我特此上传此文件orders_123.xlsx
  • 服务器:尊敬的客户,谢谢您,我存储了您的文件并创建了Job一个id = 27. 您的文件有total = 623行,当前的progress = 0.
  • 客户: 亲爱的服务器,谢谢您,请用 执行此Job操作id = 27,取200行。尽快回复。
  • 服务器:亲爱的客户,我按照你的要求做了,你现在已经Job有了。id = 27progress = 200
  • 客户端:好的,服务器,请,Job一次又一次地执行此200操作。
  • 就这样继续下去,直到工作完成。

您可能会问为什么我这样做很奇怪,而不是仅仅要求服务器导入所有行,但在这里,我再次发现这里涉及到一些黑魔法,并且这种方式是它大部分时间的唯一方式(否则服务器会失败)。

JavaScript

function uploadFile(file) {
    var data = new FormData();
    data.append("file", file);
    showProgressBar(file.name);
    $.ajax({
        type: "POST",
        url: "/import/orders",
        data: data,
        cache: false,
        processData: false,
        contentType: false,
        success: function(response) {
            if (response.status == "error") {
                hideProgressBar(file.name + ": Error! " + response.data, response.status);  
            } else if (response.status == "success") {
                executeJob(response.job, 100);
            }
        },
        xhr: function() {
            var xhr = $.ajaxSettings.xhr();
            if (typeof xhr.upload === "object") {
                xhr.upload.addEventListener("progress", function(e) {
                    if (e.lengthComputable) {
                        var val = Math.floor(100 * e.total / e.loaded)
                        updateProgressBar(val);
                    }
                }, false);
            }
            return xhr;
        }
    });
}

function executeJob(job, take) {
    $.ajax({
        type: "POST",
        url: "/jobs/execute",
        data: {
            job: job,
            take: take
        },
        success: function(response) {
            if (response.status == "error") {
                hideProgressBar(job.original_name + ": Error! " + response.data, response.status);
            } else if (response.status == "success") {
                updateProgressBar(Math.floor(100 * response.job.progress / response.job.total));
                if (val >= 100) {
                    hideProgressBar(job.original_name + ": Success!", response.status)
                    deleteJob(job);
                } else {
                    executeJob(job, take);
                }
            }
        }
    }, "json");
}

路线

Route::post('/import/orders', array('before' => 'csrf', 'uses' => 'OrdersFPController@handleOrdersImport'));
Route::post('/jobs/execute', array('before' => 'csrf', 'uses' => 'JobsController@handleExecute'));
Route::post('/jobs/delete', array('before' => 'csrf', 'uses' => 'JobsController@handleDelete'));

OrdersFPController@handleOrdersImport

class OrdersFPController extends BaseController {

    public function handleOrdersImport()
    {
        $file = Input::file('file');
        $fields = ['order', 'location', ...];

        if (!$file->isValid()) {
            return Response::json(array('status' => 'error', 'data' => 'File is invalid.'));
        }

        $filename = $file->getClientOriginalName();
        $extension = $file->getClientOriginalExtension();
        $extension_guessed = $file->guessExtension();

        if ($extension != $extension_guessed) {
            return Response::json(array('status' => 'error', 'data' => 'Wrong extension of the file: ".' . $extension . '", should be ".' . $extension_guessed . '".'));
        }

        $filename_new = str_random(20) . '.' . $extension;
        $path = public_path() . '/assets/import/orders';
        $file->move($path, $filename_new);

        $sheet = Excel::load($path . '/' . $filename_new, function($reader) {})->get();

        if (is_null($sheet)) {
            File::delete($path . '/' . $filename_new);
            return Response::json(array('status' => 'error', 'data' => 'Could not load any sheets in the file.'));
        }

        $job_total = $sheet->count();
        if ($job_total < 1) {
            File::delete($path . '/' . $filename_new);
            return Response::json(array('status' => 'error', 'data' => 'No data could be read in the file.'));
        }

        $sample = $sheet[0];

        foreach($fields as $f) {
            if (!isset($sample->$f)) {
                File::delete($path . '/' . $filename_new);
                return Response::json(array('status' => 'error', 'data' => 'Fields are missing for the selected type.'));
            }
        }


        $job = new Job;
        $job->type = 'orders';
        $job->link = $path . '/' . $filename_new;
        $job->original_name = $filename;
        $job->total = $job_total;
        $job->user()->associate(Auth::user());
        $job->save();

        return Response::json(array('status' => 'success', 'job' => $job, 'data' => 'File uploaded.'));
    }

}

问题是有时当脚本到达时$sheet = Excel::load($path . '/' . $filename_new, function($reader) {})->get();,服务器返回Error 500 (Internal server error)。有时它甚至会杀死我的服务器(不是开玩笑),停止php artisan serve终端中的命令。一些截图:

苹果浏览器

野生动物园 1 http://i.stack.imgur.com/3gkpr.png

野生动物园 2 http://i.stack.imgur.com/m8V4s.png

火狐

火狐 1 http://i.stack.imgur.com/QVceP.png

火狐 2 http://i.stack.imgur.com/IcOQF.png

火狐 3 http://i.stack.imgur.com/xvFoN.png

火狐 4 http://i.stack.imgur.com/flx3K.png

好吧,这就是问题所在。服务器什么也不返回,只是一个错误,没有描述。

编辑:

正如@lukasgeiter 所提到的,我检查了日志文件。当代码为Excel::filter('chunk')->load($path . '/' . $filename_new)->chunk(50, function($results) { /// });时,输出如下:

[2015-01-28 20:00:02] production.ERROR: exception 'Symfony\Component\Debug\Exception\FatalErrorException' with message 'Maximum execution time of 60 seconds exceeded' in /Users/antonsinyakin/Documents/projects/sites/foodpanda/vendor/phpoffice/phpexcel/Classes/PHPExcel/Reader/Excel2007.php:834
Stack trace:
#0 [internal function]: Illuminate\Exception\Handler->handleShutdown()
#1 {main} [] []

如果使用常规$sheet = Excel::load($path . '/' . $filename_new, function($reader) {})->get();,则不会将任何内容写入日志文件。

4

0 回答 0