在CakePHP 3.5.13中,我有一个Controller动作,如下所示:
class ReportController extends AppController
{
public function download($id, $format)
{
}
}
我想做的是将报表ID和格式(例如XLSX,CSV)的参数传递给download
函数,例如/report/download/123/csv
将获取报告123的数据并将其输出为CSV。
我在数组$data
中拥有报告所需的所有数据。我想创建单独的模板(.ctp文件)以将数据设置为所需的每种格式,然后将download()
函数将其发送到浏览器进行下载。
所以我对我的功能进行了如下修改:
public function download($id, $format)
{
$data = // ... Model data in array format
$filename = '123'; // filename for the download
switch ($format):
case 'csv':
header('Content-type: text/csv');
header('Content-Disposition: attachment; filename="'.$filename.'.csv"');
$this->viewBuilder()->setLayout('ajax');
$this->set('data', $data);
$this->render('download_csv');
break;
endswitch;
}
这可以正确呈现我从Template/Report/download_csv.ctp
获得的模板。它还可以在ajax布局中正确呈现($this->viewBuilder()->setLayout('ajax');
表示它使用Template/Layout/ajax.ctp
,因此不包含任何“包装” HTML)。
访问/report/download/123/csv
时,浏览器会发送一个名为123.csv
的文件,其中包含我请求的数据。但是,响应类型是text/html
,而不是我的控制器操作中指定的text/csv
。
这导致文件内容格式错误-将其视为HTML而不是CSV。例如,如果我的download_csv.ctp
模板中包含以下内容:
"Field 1", "123\n456",
"Field 2", "789"
它的输出如Excel中所示:
我猜这个问题是由于响应实际上是HTML而不是CSV,但我不确定。
我的问题是,是否可以通过这种方式使用.ctp文件,并以指定格式将它们作为下载内容输出到浏览器?
我知道使用路由和URL扩展可以做其他事情,但是这个问题与这些无关。我也不想使用插件/扩展。我想知道是否可以使用$this->render()
并让Cake渲染一个模板,该模板以请求的格式发送到浏览器以供下载?
答案 0 :(得分:3)
调用render()
只会渲染给定的模板,并使用渲染的数据填充响应主体,而与响应将要发生的情况以及发送内容的类型无关,因此答案是是的,您可以渲染模板并将其作为下载。
Excel无法按预期导入数据,这很可能与CakePHP无关,因为CSV文件不包含任何内容类型信息。也许在导入文件时选择了错误的文本限定符("
)。
也就是说,控制器不应该输出包含标头的数据!在控制器中输出数据会导致各种问题,从测试环境中无法识别的数据到无法发送的报头,甚至数据都被切断。而是使用适当的响应对象方法相应地准备下载响应,在这种情况下,您正在寻找withType()
和withDownload()
,例如:
case 'csv':
$this->response = $this->response->withType('csv');
$this->response = $this->response->withDownload($filename. '.csv');
$this->viewBuilder()->setLayout('ajax');
$this->set('data', $data);
$this->render('download_csv');
break;
这将添加正确的Content-Type
和Content-Disposition
标头。
另请参阅: