使用Laravel将表格下载为CSV格式

Jav*_*bra 35 php csv download laravel laravel-4

我试图用导出数据库表Laravelcsv文件.我希望用户能够选择Export as CSV按钮并将表格下载为csv文件.目前我已经得到了这段代码,但它无法正常工作:

我的按钮:

<a href="/all-tweets-csv" class="btn btn-primary">Export as CSV</a>
Run Code Online (Sandbox Code Playgroud)

我的路线:

Route::get('/all-tweets-csv', function(){

    $table = Tweet::all();
    $filename = "tweets.csv";
    $handle = fopen($filename, 'w+');
    fputcsv($handle, array('tweet text', 'screen name', 'name', 'created at'));

    foreach($table as $row) {
        fputcsv($handle, array($row['tweet_text'], $row['screen_name'], $row['name'], $row['created_at']));
    }

    fclose($handle);

    $headers = array(
        'Content-Type' => 'text/csv',
    );

    return Response::download($handle, 'tweets.csv', $headers);
});
Run Code Online (Sandbox Code Playgroud)

它返回给我这个错误:

 The file "Resource id #154" does not exist
Run Code Online (Sandbox Code Playgroud)

我收集到的是因为它试图下载一个不存在的文件.有没有其他方法可以修改我的代码,以便下载为csv.

Eri*_*rik 63

我在这里偶然发现,试图看看Laravel是否有默认内置的东西 - 这个问题的答案让我有点担心.我同意@andré-daniel说正确的方法是不首先编写文件,但是他的实现是手动将值组合在一起,如果任何值包含引号,空格等,则会失败.

这是一个更强大的解决方案,使用Laravel Response::stream和php fputcsv来正确格式化每一行(将转义引号,并引用必要的字符串.有关详细信息,请参阅http://php.net/manual/en/function.fputcsv.php)

<?php

public function download()
{
    $headers = [
            'Cache-Control'       => 'must-revalidate, post-check=0, pre-check=0'
        ,   'Content-type'        => 'text/csv'
        ,   'Content-Disposition' => 'attachment; filename=galleries.csv'
        ,   'Expires'             => '0'
        ,   'Pragma'              => 'public'
    ];

    $list = User::all()->toArray();

    # add headers for each column in the CSV download
    array_unshift($list, array_keys($list[0]));

   $callback = function() use ($list) 
    {
        $FH = fopen('php://output', 'w');
        foreach ($list as $row) { 
            fputcsv($FH, $row);
        }
        fclose($FH);
    };

    return Response::stream($callback, 200, $headers);
}
Run Code Online (Sandbox Code Playgroud)

  • 这不再有效..现在你可以使用响应助手...如`response() - > stream($ callback,200,$ headers)`或直接使用symfony`StreamedResponse`类. (9认同)

Mar*_*łek 25

除了这一行,几乎一切都很好:

return Response::download($handle, 'tweets.csv', $headers);
Run Code Online (Sandbox Code Playgroud)

您应该将此行更改为:

return Response::download($filename, 'tweets.csv', $headers);
Run Code Online (Sandbox Code Playgroud)

  • 你的答案有效,但作者对问题的解决方法很糟糕; 我建议在内存中创建csv并从那里创建csv,而不是创建一个文件并诱导不必要的磁盘IO +如果两个人在同一时间点击该路由可能会出现问题. (6认同)
  • @AndréDaniel,你是对的,+ 1,但我们不知道代码究竟发生了什么(这段代码可能只是一个样本)和正确的下载文件的解决方案就像我展示的那样 (2认同)
  • @ user2629998在内存中创建csv会将csv大小限制为可用内存.不要以为我们可以/应该假设csv文件很小.每当有人点击路线时,他总是可以生成随机文件名,以防止同时呼叫出现问题. (2认同)
  • @MaurizioBrioschi我为什么要这样?我正在回答OP问题而没有回答什么是最好的方法.您还会看到这个问题来自2014年和Laravel 4,对吧? (2认同)

小智 16

编辑:看到这个答案,以获得更好的解决方案; 我将在下面保留我的答案,但请注意,如果生成大文件,它会出现诸如无法转义值和使用不合理的内存量等问题.

你不必要地在磁盘上创建一个文件; 如果两个人在同一时间请求该URL,则会导致磁盘IO并导致问题(框架的两个实例将写入同一个文件,并且会发生诸如服务损坏的文件或因异常而崩溃).

请改用:

Route::get('/all-tweets-csv', function() {
    $tweets = Tweets::all();

    // the csv file with the first row
    $output = implode(",", array('tweet text', 'screen name', 'name', 'created at'));

    foreach ($tweets as $row) {
        // iterate over each tweet and add it to the csv
        $output .=  implode(",", array($row['tweet_text'], $row['screen_name'], $row['name'], $row['created_at'])); // append each row
    }

    // headers used to make the file "downloadable", we set them manually
    // since we can't use Laravel's Response::download() function
    $headers = array(
        'Content-Type' => 'text/csv',
        'Content-Disposition' => 'attachment; filename="tweets.csv"',
        );

    // our response, this will be equivalent to your download() but
    // without using a local file
    return Response::make(rtrim($output, "\n"), 200, $headers);
});
Run Code Online (Sandbox Code Playgroud)