【问题标题】:Symfony 1.4: Best way provide a file download without using a template/viewSymfony 1.4:不使用模板/视图提供文件下载的最佳方式
【发布时间】:2013-01-03 16:11:04
【问题描述】:

当然,其他人已经在 stackoverflow 上讨论过这些问题,但并非所有分析器都适合我,而且他们通常不提供 symfony 安装的版本。

我阅读的主题:

这就是我要问您如何在 symfony 1.4 中处理文件下载(不使用视图)? 在我所有的用例中,我需要一个模板文件来呈现响应。如果我发送由于控制器的响应,则唯一的可能性是在没有 php 错误(已发送标头)的情况下发送它

控制者:

/** @var $response sfWebResponse */
$response = $this->getResponse();
$response->clearHttpHeaders();
$response->setContentType($mimeType);
$response->setHttpHeader('Content-Disposition', 'attachment; filename="' . basename($filePath) . '"');
$response->setHttpHeader('Content-Description', 'File Transfer');
$response->setHttpHeader('Content-Transfer-Encoding', 'binary');
$response->setHttpHeader('Content-Length', filesize($filePath));
$response->setHttpHeader('Cache-Control', 'public, must-revalidate');
$response->setHttpHeader('Pragma', 'public');
$response->sendHttpHeaders();

readfile($filePath); die();

这无需模板文件即可工作。但是恕我直言,这不是那么漂亮的编码。

模板的替代方式:

控制者:

 /** @var $response sfWebResponse */
$response = $this->getResponse();
$response->clearHttpHeaders();
$response->setContentType($mimeType);
$response->setHttpHeader('Content-Disposition', 'attachment; filename="' . basename($filePath) . '"');
$response->setHttpHeader('Content-Description', 'File Transfer');
$response->setHttpHeader('Content-Transfer-Encoding', 'binary');
$response->setHttpHeader('Content-Length', filesize($filePath));
$response->setHttpHeader('Cache-Control', 'public, must-revalidate');
$response->setHttpHeader('Pragma', 'public');
$response->setContent(file_get_contents($filePath));
$response->sendHttpHeaders();

return sfView::NONE;

查看:

<?php echo $sf_response->getRawValue()->getContent(); ?>

【问题讨论】:

  • 你试过用return $this-&gt;renderText($response-&gt;getContent());代替return sfView::NONE;吗?
  • readfile 方法比将内容发送到视图更好,因为前者具有内置缓冲,而后者必须完全保存在内存中,因此会更慢。跨度>
  • return $this-&gt;renderText($response-&gt;getContent()); 在我的情况下不起作用。它不会引发任何异常,但会从浏览器中引发“找不到文件”错误。

标签: php symfony1 symfony-1.4


【解决方案1】:

我喜欢的解决方案

$filePath = $document->getAbsoluteFilePath();
$mimeType = mime_content_type($filePath);

/** @var $response sfWebResponse */
$response = $this->getResponse();
$response->clearHttpHeaders();
$response->setContentType($mimeType);
$response->setHttpHeader('Content-Disposition', 'attachment; filename="' . basename($filePath) . '"');
$response->setHttpHeader('Content-Description', 'File Transfer');
$response->setHttpHeader('Content-Transfer-Encoding', 'binary');
$response->setHttpHeader('Content-Length', filesize($filePath));
$response->setHttpHeader('Cache-Control', 'public, must-revalidate');
// if https then always give a Pragma header like this  to overwrite the "pragma: no-cache" header which
// will hint IE8 from caching the file during download and leads to a download error!!!
$response->setHttpHeader('Pragma', 'public');
//$response->setContent(file_get_contents($filePath)); # will produce a memory limit exhausted error
$response->sendHttpHeaders();

ob_end_flush();
return $this->renderText(readfile($filePath));

无需使用模板文件。 symfony 标准行为的使用。重要提示:模板文件必须存在!

【讨论】:

  • 优秀。注意模板文件实际上不需要存在。
  • 如果您使用模板配置模块,那么它必须存在。只有您将它们设置为 false 或在控制器中为 null,您不需要模板文件,否则将记录警告(之前发送的标头)
【解决方案2】:

根据文件的内容,我使用了两种方法。对于 Excel 文档等文档,我通常使用这种方法:

$this->getResponse()->clearHttpHeaders();
$this->getResponse()->setHttpHeaders('Content-Description','File Transer');
$this->getResponse()->setHttpHeaders('Content-Type','application/vnd.ms-excel'); //this would be based on your file
$this->getResponse()->setHttpHeaders('Content-Disposition','attachment;filename='.$filename); //$filename is name of file on server
$this->getResponse()->setHttpHeaders('Pragma','');
$this->getResponse()->setHttpHeaders('Cache-Control','');
$this->getResponse()->sendHttpHeaders();

$error_reporting = error_reporting(0);
$this->renderText($some_data->save('php://output')); //in this case the $some_data was a PHPExcel writer object but anything that can be saved to a [php://output][1] should work e.g. fwrite()
error_reporting($error_reporting);
return sfView::NONE

error_reporting 的关闭和打开与使用 PHPExcel 写入流有关。

我使用的另一种方法是使用sfResponsesendContent() 方法。这种用法的例子是:

$this->getResponse()->clearHttpheaders();
$this->getResponse()->setHttpHeader('Content-Description','File Transfer');
$this->getResponse()->setHttpHeader('Cache-Control', 'public, must-revalidate, max-age=0');
$this->getResponse()->setHttpHeader('Pragma: public',true);
$this->getResponse()->setHttpHeader('Content-Transfer-Encoding', 'binary'); 
$this->getResponse()->setHttpHeader('Content-length',filesize($filename)) //send the size of the file
$this->getResponse()->setHttpHeader('Content-Type','some_mime_type') // e.g. application/pdf, image/png etc.
$this->getResponse()->setHttpHeader('Content-Disposition','attachment; filename='.$filename) //some filename
$this->getResponse()->sendHttpHeaders(); //edited to add the missed sendHttpHeaders
$this->getResponse()->setContent(readfile($filename));

$this->getResponse()->sendContent();

return sfView::NONE;

这两种方法都有效,您不需要模板来呈现内容/文件。

注意:编辑在设置和发送内容之前添加$this-&gt;getResponse()-&gt;sendHttpHeaders()

【讨论】:

  • 您的第二个解决方案将有效,但会使用PHP Warning: Cannot modify header information - headers already sent 消息填充错误日志。所以这不是一个很好的解决方案。只有在 sendContend() 之后使用 die() 退出输出时,才不会抛出警告。
  • 抱歉,错过了$this-&gt;getResponse()-&gt;sendHttpHeaders()。我已经编辑了答案以包含它。
  • 这不是重点。使用$this-&gt;getResponse()-&gt;sendContent();,您将发送标头,除非您之前没有发送标头,但使用 return 语句恕我直言,将发送下一个标头,然后引发警告。
  • 好吧,我不太确定你在说什么。我发送标头,然后发送内容,没有收到任何 PHP 错误。您确定您的代码中没有发生其他事情吗?
【解决方案3】:

你也可以用普通的 php 函数来做:

public function executeIndex(sfWebRequest $request)
{
  while(ob_get_level())
  {
    ob_end_clean();
  }

  // use plain php functions to
  // setup headers
  // ...
  // and read and echo the file

  throw new sfStopException();
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-25
    • 1970-01-01
    • 2019-03-05
    • 1970-01-01
    • 1970-01-01
    • 2014-01-27
    相关资源
    最近更新 更多