laravel 文件下载文件导出为csv格式

laravel 文件下载导出

依赖 laravel-admin

自定义按钮文件
文件名: App\Admin\Extensions\Tools\ButtonLink.php

自定义导出文件
文件名: App\Admin\Extensions\Exporters\CsvExporter.php

在 admin 的route文件 中加路由
文件名: App\Admin\routes.php

在 admin 的控制器文件 中使用
文件名: App\Admin\Controllers\JiaZuV2GuanXiExportController.php

4个文件的大致文件内容

自定义按钮文件:App\Admin\Extensions\Tools\ButtonLink.php

<?php

namespace App\Admin\Extensions\Tools;

use Encore\Admin\Admin;
use Encore\Admin\Grid\Tools\AbstractTool;

class ButtonLink extends AbstractTool
{

    public $buttonElementClass = '';
    public $btnUrl = '';
    public $addGridRoute = true;
    public $btnName = '';

    public function getElementClass() {
        return $this->buttonElementClass;
    }

    public function setElementClass($className) {
        $this->buttonElementClass = $className;
        return $this;
    }

    public function getBtnUrl() {
        return $this->btnUrl;
    }

    public function setBtnUrl($url) {
        $this->btnUrl = $url;
        return $this;
    }

    public function getAddGridRoute() {
        return $this->addGridRoute;
    }

    public function setAddGridRoute($addGridRoute) {
        $this->addGridRoute = $addGridRoute;
        return $this;
    }

    public function getBtnName() {
        return $this->btnName;
    }

    public function setBtnName($name) {
        $this->btnName = $name;
        return $this;
    }

    protected function getBtnUrlReal() {
        $url = $this->getBtnUrl();
        if ($this->getAddGridRoute()) {
            $url = $this->grid->resource().$this->getBtnUrl();
        }
        return $url;
    }

    protected function script() {
        return <<<EOT

$('.{$this->getElementClass()}').on('click', function() {
    var url = '{$this->getBtnUrlReal()}';
    var name = '{$this->getBtnName()}';
    window.open(url, name);
});

EOT;
    }

    public function render() {
        Admin::script($this->script());
        return <<<HTML
        <a class="btn btn-sm btn-default {$this->getElementClass()}">{$this->getBtnName()}</a>
HTML;
    }
}

自定义导出文件:App\Admin\Extensions\Exporters\CsvExporter.php

<?php

namespace App\Admin\Extensions\Exporters;

use Encore\Admin\Grid\Column;
use Encore\Admin\Grid\Exporters\AbstractExporter;
use Illuminate\Database\Eloquent\Model;

class CsvExporter extends AbstractExporter
{
    /**
     * @var string
     */
    protected $filename;

    /**
     * @var []\Closure
     */
    protected $columnCallbacks;

    /**
     * @var []\Closure
     */
    protected $titleCallbacks;

    /**
     * @var array
     */
    protected $visibleColumns;

    /**
     * 时间
     */
    protected $time;

    /**
     * model
     */
    protected $model;

    /**
     * 分块处理数据 行数
     */
    protected $chunkNum;

    /**
     * Create a new exporter instance.
     *
     * 我不要grid
     */
    public function __construct()
    {
        set_time_limit(0);
        // 调整 内存大小
        ini_set('memory_limit', '1G');
        $this->setTime();
        $this->setChunkNum(100);
    }

    /**
     * 执行时间
     */
    public function setTime($now = false) {
        if ($now) {
            $this->time = time();
        }
        if (empty($this->time)) {
            $this->time = time();
        }
    }

    /**
     * 获取时间
     */
    public function getTimeFormat() {
        $this->setTime();
        return date('Y-m-d H:i:s', $this->time);
    }

    /**
     * 分块处理数据 行数
     */
    public function setChunkNum($num = 100): self
    {
        $num = intval($num);
        if ($num < 1) $num = 1;
        if ($num > 10000) $num = 10000;
        $this->chunkNum = $num;

        return $this;
    }

    /**
     * 分块处理数据 行数
     */
    public function getChunkNum() {
        $num = intval($this->chunkNum);
        if ($num < 1) $num = 1;
        if ($num > 10000) $num = 10000;
        return $num;
    }

    /**
     * @param string $filename
     *
     * @return $this
     */
    public function filename(string $filename = ''): self
    {
        $this->filename = $filename;

        return $this;
    }

    /**
     * @param string   $name
     * @param \Closure $callback
     *
     * @return $this
     */
    public function column(string $name, \Closure $callback): self
    {
        $this->columnCallbacks[$name] = $callback;

        return $this;
    }

    /**
     * @param string   $name
     * @param \Closure $callback
     *
     * @return $this
     */
    public function title(string $name, \Closure $callback): self
    {
        $this->titleCallbacks[$name] = $callback;

        return $this;
    }

    /**
     * Get download response headers.
     *
     * @return array
     */
    protected function getHeaders()
    {
        if (!$this->filename) {
            $this->filename = $this->getTimeFormat();
        }

        return [
            'Content-Encoding'    => 'UTF-8',
            'Content-Type'        => 'text/csv;charset=UTF-8',
            'Content-Disposition' => "attachment;filename=\"{$this->filename}.csv\"",
        ];
    }

    /**
    * 设置数据源(支持模型类或查询构建器)
    * 
    * @param  Model|Builder  $source
    * @return self
    */
    public function setModel($source): self
    {
        if ($source instanceof Model) {
            $this->model = $source->query();
        } else {
            $this->model = $source;
        }

        return $this;
    }

    /**
    * 设置要导出的字段(支持关联模型字段)
    * 
    * @param  array  $columns 如 ['id', 'profile.age']
    * @return self
    */
    public function setColumns(array $columns): self
    {
        $this->visibleColumns = $columns;
        return $this;
    }

    /**
    * 设置自定义表头
    * 
    * @param  array  $titles
    * @return self
    */
    public function setTitles(array $titles): self
    {
        $this->titleCallbacks = $titles;
        return $this;
    }

    /**
    * 核心导出逻辑重构
    */
    public function export_ai()
    {
        return new StreamedResponse(function () {
            $handle = fopen('php://output', 'w');
            fwrite($handle, chr(0xEF).chr(0xBB).chr(0xBF));

            // 生成表头
            fputcsv($handle, $this->buildHeaders());

            // 分块处理数据
            $this->model->chunk(1000, function ($records) use ($handle) {
                foreach ($records as $record) {
                    fputcsv($handle, $this->buildRow($record));
                }
            });

            fclose($handle);
        }, 200, $this->getHeaders());
    }

    /**
    * 构建CSV表头
    */
    protected function buildHeaders(): array
    {
        // 优先使用自定义标题
        if (!empty($this->titleCallbacks)) {
            return $this->titleCallbacks;
        }

        // 自动生成字段名称
        return array_map(function($column) {
            return ucwords(str_replace(['_', '.'], ' ', $column));
        }, $this->visibleColumns);
    }

    /**
    * 构建单行数据
    */
    protected function buildRow($record): array
    {
        $row = [];

        foreach ($this->visibleColumns as $column) {
            $value = $this->getColumnValue($record, $column);

            // 处理回调函数
            if (isset($this->columnCallbacks[$column])) {
                $value = call_user_func($this->columnCallbacks[$column], $value, $record);
            }

            $row[] = $value;
        }

        return $row;
    }

    /**
    * 获取字段值(支持关联模型)
    */
    protected function getColumnValue($record, string $column)
    {
        // 处理关联字段(如profile.age)
        if (str_contains($column, '.')) {
            return data_get($record, $column);
        }

        // 原始字段值
        if (gettype($record) === 'object') {
            return $record->$column;
        }
        return $record->getAttribute($column);
    }

    /**
    * 核心导出逻辑重构
    */
    public function export_debug()
    {
        $back = [];
        $back[] = $this->buildHeaders();
        // 分块处理数据 必须指定 orderBy
        $this->model->chunk(100, function ($records) use (&$back) {
            foreach ($records as $record) {
                $back[] = $this->buildRow($record);
            }
        });
        dd($back);
    }

    /**
     * {@inheritdoc}
     */
    public function export()
    {
        $chunkNum = $this->getChunkNum();
        $response = function () use ($chunkNum) {
            $handle = fopen('php://output', 'w');
            $titles = [];
            fwrite($handle, chr(0xEF).chr(0xBB).chr(0xBF)); //导出的CSV文件是无BOM编码UTF-8,而我们通常使用UTF-8编码格式都是有BOM的。所以添加BOM于CSV中

            // 生成表头
            fputcsv($handle, $this->buildHeaders());

            // 分块处理数据
            $this->model->chunk($chunkNum, function ($records) use ($handle) {
                foreach ($records as $record) {
                    fputcsv($handle, $this->buildRow($record));
                }
            });

            fclose($handle);
        };

        response()->stream($response, 200, $this->getHeaders())->send();

        exit;
    }
}

在 admin 的route文件 中加路由文件:App\Admin\routes.php

<?php

use Illuminate\Routing\Router;

Admin::routes();

Route::group([
    'prefix'        => config('admin.route.prefix'),
    'namespace'     => config('admin.route.namespace'),
    'middleware'    => config('admin.route.middleware'),
    'as'            => config('admin.route.prefix') . '.',
], function (Router $router) {
    $router->get('/', 'HomeController@index')->name('home');

    $router->resource('jia-zu-v2-guan-xi-exports', JiaZuV2GuanXiExportController::class);
    $router->any('jia-zu-v2-guan-xi-exports-action/export1', 'JiaZuV2GuanXiExportController@export1');

});

在 admin 的控制器文件 中使用文件:App\Admin\Controllers\JiaZuV2GuanXiExportController.php

<?php

namespace App\Admin\Controllers;

use Encore\Admin\Controllers\AdminController;
use Encore\Admin\Form;
use Encore\Admin\Grid;
use Encore\Admin\Show;

class JiaZuV2GuanXiExportController extends AdminController
{
    /**
     * Title for current resource.
     *
     * @var string
     */
    protected $title = '导出';

    /**
     * Make a grid builder.
     *
     * @return Grid
     */
    protected function grid()
    {
        $grid = new Grid(new JiaZuV2GuanXi());

        $grid->column('id', __('Id'))->sortable();
        $grid->column('created_at', __('Created at'))->display(function(){
            return $this->created_at;
        });
        $grid->column('updated_at', __('Updated at'))->display(function(){
            return $this->updated_at;
        });

        // 将 导出 操作加入到表格的工具条中
        $grid->tools(function ($tools) {
            $btn1 = new \App\Admin\Extensions\Tools\ButtonLink();
            $btn1->setBtnName('导出(需要的数据格式1)')->setElementClass('jia-zu-guan-xi-export-export1')->setBtnUrl('-action/export1');
            $tools->append($btn1);
        });

        $grid->disableCreateButton();
        $grid->disableRowSelector();
        // $grid->disableExport();
        $grid->export(function($export){
            set_time_limit(0);
            $export->except(['touxiang']);
        });

        return $grid;
    }

    /**
     * Make a show builder.
     *
     * @param mixed $id
     * @return Show
     */
    protected function detail($id)
    {

    }

    /**
     * Make a form builder.
     *
     * @return Form
     */
    protected function form()
    {

    }

    /**
     * 导出-需要的数据格式
     */
    public function export1(\Request $request) {
        // 类型
        $type = JiaZuV2List::gYuYueType();
        if (empty($type)) return abort(200, '无类型');
        $tableNameJiaZuGuanXiExport = JiaZuV2GuanXi::getModel()->getTable();
        $tableNameWxXcxYuYueAccountRecords = WxXcxYuyueAccountRecords::getModel()->getTable();
        // 用 union
        $left = \DB::table($tableNameJiaZuGuanXiExport . ' as t1j')
            ->leftJoin($tableNameWxXcxYuYueAccountRecords. ' as t1y', function($join) use($type) {
                $join->on('t1j.m_openid', '=', 't1y.account_openid')
                    ->where('t1y.yuyue_type', $type);
            })
            ->select('t1j.jiazu_id', 't1j.m_openid', 't1j.title', 't1j.zu_zhang', 't1y.yuyue_zhuangtai', 't1j.uid');

        $right = \DB::table($tableNameWxXcxYuYueAccountRecords.' as t2y')
            ->leftJoin($tableNameJiaZuGuanXiExport.' as t2j', 't2j.m_openid', '=', 't2y.account_openid')
            ->where('t2y.yuyue_type', $type)
            ->select('t2j.jiazu_id', 't2y.account_openid as m_openid', 't2j.title', 't2j.zu_zhang', 't2y.yuyue_zhuangtai', 't2y.account_uid as uid');

        // chunk 分块处理数据 必须指定 orderBy
        $query = $left->union($right)->orderBy('m_openid', 'asc');
        $exporter = new \App\Admin\Extensions\Exporters\CsvExporter();
        $exporter->setModel($query)
            ->setColumns(['jiazu_id', 'oid', 'title'])
            ->setTitles(['id', 'oid', 'title'])
            ->filename('导出(需要的数据格式1)')
            ->column('jiazu_id', function($value) {
                $value = trim($value);
                if ($value === '') return '';
                return $value;
            })
            // chunk 分块处理数据 每次查询并返回1000条数据
            ->setChunkNum(1000)
            ->export();
        die();
    }
}

CsvExporter.php可以去掉laravel-admin的依赖

自定义导出文件:App\Admin\Extensions\Exporters\CsvExporter.php 做如下修改

laravel 文件下载文件导出为csv格式

最终文件内容为:

<?php

namespace App\Admin\Extensions\Exporters;

use Illuminate\Database\Eloquent\Model;

class CsvExporter
{
    /**
     * @var string
     */
    protected $filename;

    /**
     * @var []\Closure
     */
    protected $columnCallbacks;

    /**
     * @var []\Closure
     */
    protected $titleCallbacks;

    /**
     * @var array
     */
    protected $visibleColumns;

    /**
     * 时间
     */
    protected $time;

    /**
     * model
     */
    protected $model;

    /**
     * 分块处理数据 行数
     */
    protected $chunkNum;

    /**
     * Create a new exporter instance.
     *
     * 我不要grid
     */
    public function __construct()
    {
        set_time_limit(0);
        // 调整 内存大小
        ini_set('memory_limit', '1G');
        $this->setTime();
        $this->setChunkNum(100);
    }

    /**
     * 执行时间
     */
    public function setTime($now = false) {
        if ($now) {
            $this->time = time();
        }
        if (empty($this->time)) {
            $this->time = time();
        }
    }

    /**
     * 获取时间
     */
    public function getTimeFormat() {
        $this->setTime();
        return date('Y-m-d H:i:s', $this->time);
    }

    /**
     * 分块处理数据 行数
     */
    public function setChunkNum($num = 100): self
    {
        $num = intval($num);
        if ($num < 1) $num = 1;
        if ($num > 10000) $num = 10000;
        $this->chunkNum = $num;

        return $this;
    }

    /**
     * 分块处理数据 行数
     */
    public function getChunkNum() {
        $num = intval($this->chunkNum);
        if ($num < 1) $num = 1;
        if ($num > 10000) $num = 10000;
        return $num;
    }

    /**
     * @param string $filename
     *
     * @return $this
     */
    public function filename(string $filename = ''): self
    {
        $this->filename = $filename;

        return $this;
    }

    /**
     * @param string   $name
     * @param \Closure $callback
     *
     * @return $this
     */
    public function column(string $name, \Closure $callback): self
    {
        $this->columnCallbacks[$name] = $callback;

        return $this;
    }

    /**
     * @param string   $name
     * @param \Closure $callback
     *
     * @return $this
     */
    public function title(string $name, \Closure $callback): self
    {
        $this->titleCallbacks[$name] = $callback;

        return $this;
    }

    /**
     * Get download response headers.
     *
     * @return array
     */
    protected function getHeaders()
    {
        if (!$this->filename) {
            $this->filename = $this->getTimeFormat();
        }

        return [
            'Content-Encoding'    => 'UTF-8',
            'Content-Type'        => 'text/csv;charset=UTF-8',
            'Content-Disposition' => "attachment;filename=\"{$this->filename}.csv\"",
        ];
    }

    /**
    * 设置数据源(支持模型类或查询构建器)
    * 
    * @param  Model|Builder  $source
    * @return self
    */
    public function setModel($source): self
    {
        if ($source instanceof Model) {
            $this->model = $source->query();
        } else {
            $this->model = $source;
        }

        return $this;
    }

    /**
    * 设置要导出的字段(支持关联模型字段)
    * 
    * @param  array  $columns 如 ['id', 'profile.age']
    * @return self
    */
    public function setColumns(array $columns): self
    {
        $this->visibleColumns = $columns;
        return $this;
    }

    /**
    * 设置自定义表头
    * 
    * @param  array  $titles
    * @return self
    */
    public function setTitles(array $titles): self
    {
        $this->titleCallbacks = $titles;
        return $this;
    }

    /**
    * 核心导出逻辑重构
    */
    public function export_ai()
    {
        return new StreamedResponse(function () {
            $handle = fopen('php://output', 'w');
            fwrite($handle, chr(0xEF).chr(0xBB).chr(0xBF));

            // 生成表头
            fputcsv($handle, $this->buildHeaders());

            // 分块处理数据
            $this->model->chunk(1000, function ($records) use ($handle) {
                foreach ($records as $record) {
                    fputcsv($handle, $this->buildRow($record));
                }
            });

            fclose($handle);
        }, 200, $this->getHeaders());
    }

    /**
    * 构建CSV表头
    */
    protected function buildHeaders(): array
    {
        // 优先使用自定义标题
        if (!empty($this->titleCallbacks)) {
            return $this->titleCallbacks;
        }

        // 自动生成字段名称
        return array_map(function($column) {
            return ucwords(str_replace(['_', '.'], ' ', $column));
        }, $this->visibleColumns);
    }

    /**
    * 构建单行数据
    */
    protected function buildRow($record): array
    {
        $row = [];

        foreach ($this->visibleColumns as $column) {
            $value = $this->getColumnValue($record, $column);

            // 处理回调函数
            if (isset($this->columnCallbacks[$column])) {
                $value = call_user_func($this->columnCallbacks[$column], $value, $record);
            }

            $row[] = $value;
        }

        return $row;
    }

    /**
    * 获取字段值(支持关联模型)
    */
    protected function getColumnValue($record, string $column)
    {
        // 处理关联字段(如profile.age)
        if (str_contains($column, '.')) {
            return data_get($record, $column);
        }

        // 原始字段值
        if (gettype($record) === 'object') {
            return $record->$column;
        }
        return $record->getAttribute($column);
    }

    /**
    * 核心导出逻辑重构
    */
    public function export_debug()
    {
        $back = [];
        $back[] = $this->buildHeaders();
        // 分块处理数据 必须指定 orderBy
        $this->model->chunk(100, function ($records) use (&$back) {
            foreach ($records as $record) {
                $back[] = $this->buildRow($record);
            }
        });
        dd($back);
    }

    /**
     * {@inheritdoc}
     */
    public function export()
    {
        $chunkNum = $this->getChunkNum();
        $response = function () use ($chunkNum) {
            $handle = fopen('php://output', 'w');
            $titles = [];
            fwrite($handle, chr(0xEF).chr(0xBB).chr(0xBF)); //导出的CSV文件是无BOM编码UTF-8,而我们通常使用UTF-8编码格式都是有BOM的。所以添加BOM于CSV中

            // 生成表头
            fputcsv($handle, $this->buildHeaders());

            // 分块处理数据
            $this->model->chunk($chunkNum, function ($records) use ($handle) {
                foreach ($records as $record) {
                    fputcsv($handle, $this->buildRow($record));
                }
            });

            fclose($handle);
        };

        response()->stream($response, 200, $this->getHeaders())->send();

        exit;
    }
}
本作品采用《CC 协议》,转载必须注明作者和本文链接
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
讨论数量: 1

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!