疑难杂症,CSV 文件下载失败问题。

前两天还好好的,今天就不能下载了。
没有报错。正常走程序,就是csv无法下载下来。
dd出来的数据如下。
file
blade如下

<form action="{{route('manage.download')}}" method="POST" enctype="multipart/form-data">

<button type="submit" class="btn btn-primary" style="margin-top:20px;">下载</button>

</form>

通用下载类如下

class csv{

    public function download($list, $header, $filename, $quot=true)
    {

        if (count($header) > 0) {
            array_unshift($list, $header);
        }
        $stream = fopen('php://temp', 'r+b');

        foreach ($list as $row) {
            $this->_fputcsv($stream, $row,$quot);
        }
        rewind($stream);
        //$csv = str_replace("\n", "\r\n", stream_get_contents($stream));
        $csv = stream_get_contents($stream);
        $headers = array(
            'Content-Type' => 'text/csv',
            'Content-Disposition' => "attachment; filename=$filename",
        );

        return \Response::make($csv, 200, $headers);
    }

    private function _fputcsv($fp, $fields, $quot) {
        $tmp = array();
        foreach ($fields as $value) {
            if($quot){
                $value = str_replace('"', '""', $value);
                $tmp[]= '"'.$value.'"';
            }
            else{
                $tmp[]= $value;
            }
        }

        $str = implode(',', $tmp);
        $str .= "\r\n";
        fputs($fp, $str);
    }
}

调用方法如下。

public function csvdownload()
    {
        //原始数据
        $data = [
                 0 => [
                    'id'    => '1',
                    'name'  => 'test1',
                ],
                1 =>[
                    'id'    => '2',
                    'name'  => 'test2',
                ],
                ....
        ];
        //CSV header
        $header = ['id', 'name'];
        //文件名
        $filename = Carbon::now()->format('YmdHis') . ".csv";

        //调用类,下载。
        CSV::download($data, $header, $filename, true);
    }
⬇︎第一次零基础搭建的个人博客。欢迎批评指正,大力鞭策!❤︎ 旺财的个人博客(⌯¤̴̶̷̀ω¤̴̶̷́)✧ January 17th, 2020
chihokyo
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
最佳答案

@chihokyo 果然如我所料,如果不在前端处理的话,据我所知后端是无法实现下载并跳转的。

你这样的方式会导致发回给前端的响应只有 view('manage.list');,而没有下载文件的响应。

实际上,在 HTTP 协议里,无论是页面还是文件下载,都是一个普通响应而已。所以你如果需要让浏览器「下载」(或者说,接收你的响应)那就必须要在控制器方法内返回该响应。

5年前 评论
讨论数量: 12

可以试着在代码的第一行 dd(1) 一直到代码最后一行,看看哪一行报错了呀

5年前 评论
chihokyo

@HI
虽然我技术一般,但是基本debug的能力还是有的。
说了,这里从头到尾没有报错。

5年前 评论

今天就不能下载了。

具体是什么表现?响应数据为空?有数据但浏览器并没有下载,而是直接显示?

5年前 评论
chihokyo

@Wi1dcard
具体表现就是感觉什么都是正确的啊。
数据没有直接显示,因为我写的方法里,下载完之后直接跳转到首页上。
按理说应该是下载然后跳转,现在的情况是直接跳转。
然后我看了一下我的写的下载方法没错,上面有写。
就是感觉没响应到下载文件这个东西,由于我也不太懂网页下载文件具体的机制,于是我就直接dd。
dd(Response::make($csv, 200, $headers));
然后就输出图片上的信息,我看输出的各种信息都正常。。

5年前 评论

@chihokyo 使用 Laravel 内置的方法试试看:https://learnku.com/docs/laravel/5.7/responses#fil...

另外,下载完之后直接跳转到首页上?不知道你是怎么实现的,有可能是因为跳转的方式不当引起。你可以尝试去掉跳转试试看。

最后,根据你不完全的描述,基本无法判断问题,建议使用 DevTool 抓包查看原始的 HTTP 请求和响应,以便调试。

5年前 评论
chihokyo

@Wi1dcard
早上好,去掉调转也是一样的结果。
跳转就是

 //调用类,下载。
 CSV::download($data, $header, $filename, true);
 //跳转
 return view('manage.list');
正常来说执行到

正常执行到

//调用类,下载。
        CSV::download($data, $header, $filename, true);

应该就下载。
但是没有。。

话说我刚才解决了:sob:,终于能下载了。随便捣鼓的。
但是解决方法让我无法理解。
为什么啊,返回就下载了。。直接调用不行么。。

 //调用类,下载。
 CSV::download($data, $header, $filename, true);

 // 修改成
 return CSV::download($data, $header, $filename, true);
5年前 评论

@chihokyo 果然如我所料,如果不在前端处理的话,据我所知后端是无法实现下载并跳转的。

你这样的方式会导致发回给前端的响应只有 view('manage.list');,而没有下载文件的响应。

实际上,在 HTTP 协议里,无论是页面还是文件下载,都是一个普通响应而已。所以你如果需要让浏览器「下载」(或者说,接收你的响应)那就必须要在控制器方法内返回该响应。

5年前 评论
chihokyo

@Wi1dcard
很谢谢你的解答,我懂了。

但是现在新的问题就是,下载之后如果我还想继续刷新页面返回其他数据呢。

5年前 评论

@chihokyo 可以参考一些下载页面的做法,例如 VSCode

先跳转到临时页面,发起下载,再跳转到你需要的页面。

以上过程可以更加简化。

例如,你点击按钮,返回一个跳转的响应,且携带一个参数(例如:?download=true)。

然后在跳转过去的请求里进行判断,是否存在 download 参数,若存在则发起下载。

或者甚至可以更简单,利用 JavaScript 发起请求而非 HTML 表单提交。

另外,对前端我不太了解,似乎现在有些技术可以实现 JavaScript 下载数据,然后再交给浏览器写入本地。你可以谷歌一下。

5年前 评论
chihokyo

@Wi1dcard
我解释了一下。
需求方表示太麻烦,然后下载无需刷新了。
直接下载就行了。:new_moon_with_face:
js不好,感觉好多事情都很难办理啊。
谢谢回复了。

5年前 评论

:grin:直接die,不return应该也可以导出。

3年前 评论

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