Commit fa0418f8 by Qiang Xue

Fixes #4424: Added `inline` and `mimeType` options to all file downloading…

Fixes #4424: Added `inline` and `mimeType` options to all file downloading methods provided in `yii\web\Response`
parent 14cd4ed9
...@@ -176,7 +176,7 @@ Yii Framework 2 Change Log ...@@ -176,7 +176,7 @@ Yii Framework 2 Change Log
- Enh #4317: Added `absoluteAuthTimeout` to yii\web\User (ivokund, nkovacs) - Enh #4317: Added `absoluteAuthTimeout` to yii\web\User (ivokund, nkovacs)
- Enh #4360: Added client validation support for file validator (Skysplit) - Enh #4360: Added client validation support for file validator (Skysplit)
- Enh #4372: `yii\filters\HttpCache` failed to comply to RFC 7232 (DaSourcerer) - Enh #4372: `yii\filters\HttpCache` failed to comply to RFC 7232 (DaSourcerer)
- Enh #4424: Added `forceDownload` parameter to `yii\web\Response::xSendFile()` (klimov-paul) - Enh #4424: Added `inline` parameter to `yii\web\Response::xSendFile()` (klimov-paul)
- Enh #4436: Added callback functions to AJAX-based form validation (thiagotalma) - Enh #4436: Added callback functions to AJAX-based form validation (thiagotalma)
- Enh #4485: Added support for deferred validation in `ActiveForm` (Alex-Code) - Enh #4485: Added support for deferred validation in `ActiveForm` (Alex-Code)
- Enh #4520: Added sasl support to `yii\caching\MemCache` (xjflyttp) - Enh #4520: Added sasl support to `yii\caching\MemCache` (xjflyttp)
...@@ -239,6 +239,7 @@ Yii Framework 2 Change Log ...@@ -239,6 +239,7 @@ Yii Framework 2 Change Log
- Chg #4310: Removed `$data` from signature of `yii\rbac\ManagerInterface` (samdark) - Chg #4310: Removed `$data` from signature of `yii\rbac\ManagerInterface` (samdark)
- Chg #4318: `yii\helpers\Html::ul()` and `ol()` will return an empty list tag if an empty item array is given (qiangxue) - Chg #4318: `yii\helpers\Html::ul()` and `ol()` will return an empty list tag if an empty item array is given (qiangxue)
- Chg #4331: `yii\helpers\Url` now uses `UrlManager` to determine base URL when generating URLs (qiangxue) - Chg #4331: `yii\helpers\Url` now uses `UrlManager` to determine base URL when generating URLs (qiangxue)
- Chg #4424: Added `inline` and `mimeType` options to all file downloading methods provided in `yii\web\Response` (qiangxue)
- Chg #4501: Renamed the constant `YII_PATH` to `YII2_PATH` (qiangxue) - Chg #4501: Renamed the constant `YII_PATH` to `YII2_PATH` (qiangxue)
- Chg #4586: Signed bigint and unsigned int will be converted into integers when they are loaded from DB by AR (qiangxue) - Chg #4586: Signed bigint and unsigned int will be converted into integers when they are loaded from DB by AR (qiangxue)
- Chg #4591: `yii\helpers\Url::to()` will no longer prefix relative URLs with the base URL (qiangxue) - Chg #4591: `yii\helpers\Url::to()` will no longer prefix relative URLs with the base URL (qiangxue)
......
...@@ -197,5 +197,8 @@ new ones save the following code as `convert.php` that should be placed in the s ...@@ -197,5 +197,8 @@ new ones save the following code as `convert.php` that should be placed in the s
* The format of the Faker fixture template is changed. For an example, please refer to the file * The format of the Faker fixture template is changed. For an example, please refer to the file
`apps/advanced/common/tests/templates/fixtures/user.php`. `apps/advanced/common/tests/templates/fixtures/user.php`.
* Signature of the `yii\web\Response::xSendFile()` method has changed. If you're using or overriding this method you * The signature of all file downloading methods in `yii\web\Response` is changed, as summarized below:
must change your code to fit it. - `sendFile($filePath, $attachmentName = null, $options = [])`
- `sendContentAsFile($content, $attachmentName, $options = [])`
- `sendStreamAsFile($handle, $attachmentName, $options = [])`
- `xSendFile($filePath, $attachmentName = null, $options = [])`
...@@ -10,6 +10,7 @@ namespace yii\web; ...@@ -10,6 +10,7 @@ namespace yii\web;
use Yii; use Yii;
use yii\base\InvalidConfigException; use yii\base\InvalidConfigException;
use yii\base\InvalidParamException; use yii\base\InvalidParamException;
use yii\helpers\ArrayHelper;
use yii\helpers\Url; use yii\helpers\Url;
use yii\helpers\FileHelper; use yii\helpers\FileHelper;
use yii\helpers\StringHelper; use yii\helpers\StringHelper;
...@@ -423,19 +424,24 @@ class Response extends \yii\base\Response ...@@ -423,19 +424,24 @@ class Response extends \yii\base\Response
* *
* @param string $filePath the path of the file to be sent. * @param string $filePath the path of the file to be sent.
* @param string $attachmentName the file name shown to the user. If null, it will be determined from `$filePath`. * @param string $attachmentName the file name shown to the user. If null, it will be determined from `$filePath`.
* @param string $mimeType the MIME type of the content. If null, it will be guessed based on `$filePath` * @param array $options additional options for sending the file. The following options are supported:
*
* - `mimeType`: the MIME type of the content. If not set, it will be guessed based on `$filePath`
* - `inline`: boolean, whether the browser should open the file within the browser window. Defaults to false,
* meaning a download dialog will pop up.
*
* @return static the response object itself * @return static the response object itself
*/ */
public function sendFile($filePath, $attachmentName = null, $mimeType = null) public function sendFile($filePath, $attachmentName = null, $options = [])
{ {
if ($mimeType === null && ($mimeType = FileHelper::getMimeTypeByExtension($filePath)) === null) { if (!isset($options['mimeType'])) {
$mimeType = 'application/octet-stream'; $options['mimeType'] = FileHelper::getMimeTypeByExtension($filePath);
} }
if ($attachmentName === null) { if ($attachmentName === null) {
$attachmentName = basename($filePath); $attachmentName = basename($filePath);
} }
$handle = fopen($filePath, 'rb'); $handle = fopen($filePath, 'rb');
$this->sendStreamAsFile($handle, $attachmentName, $mimeType); $this->sendStreamAsFile($handle, $attachmentName, $options);
return $this; return $this;
} }
...@@ -448,11 +454,16 @@ class Response extends \yii\base\Response ...@@ -448,11 +454,16 @@ class Response extends \yii\base\Response
* *
* @param string $content the content to be sent. The existing [[content]] will be discarded. * @param string $content the content to be sent. The existing [[content]] will be discarded.
* @param string $attachmentName the file name shown to the user. * @param string $attachmentName the file name shown to the user.
* @param string $mimeType the MIME type of the content. * @param array $options additional options for sending the file. The following options are supported:
*
* - `mimeType`: the MIME type of the content. Defaults to 'application/octet-stream'.
* - `inline`: boolean, whether the browser should open the file within the browser window. Defaults to false,
* meaning a download dialog will pop up.
*
* @return static the response object itself * @return static the response object itself
* @throws HttpException if the requested range is not satisfiable * @throws HttpException if the requested range is not satisfiable
*/ */
public function sendContentAsFile($content, $attachmentName, $mimeType = 'application/octet-stream') public function sendContentAsFile($content, $attachmentName, $options = [])
{ {
$headers = $this->getHeaders(); $headers = $this->getHeaders();
...@@ -464,7 +475,8 @@ class Response extends \yii\base\Response ...@@ -464,7 +475,8 @@ class Response extends \yii\base\Response
throw new HttpException(416, 'Requested range not satisfiable'); throw new HttpException(416, 'Requested range not satisfiable');
} }
$this->setDownloadHeaders($attachmentName, $mimeType, $contentLength); $mimeType = isset($options['mimeType']) ? $options['mimeType'] : 'application/octet-stream';
$this->setDownloadHeaders($attachmentName, $mimeType, !empty($options['inline']), $contentLength);
list($begin, $end) = $range; list($begin, $end) = $range;
if ($begin != 0 || $end != $contentLength - 1) { if ($begin != 0 || $end != $contentLength - 1) {
...@@ -489,11 +501,16 @@ class Response extends \yii\base\Response ...@@ -489,11 +501,16 @@ class Response extends \yii\base\Response
* *
* @param resource $handle the handle of the stream to be sent. * @param resource $handle the handle of the stream to be sent.
* @param string $attachmentName the file name shown to the user. * @param string $attachmentName the file name shown to the user.
* @param string $mimeType the MIME type of the stream content. * @param array $options additional options for sending the file. The following options are supported:
*
* - `mimeType`: the MIME type of the content. Defaults to 'application/octet-stream'.
* - `inline`: boolean, whether the browser should open the file within the browser window. Defaults to false,
* meaning a download dialog will pop up.
*
* @return static the response object itself * @return static the response object itself
* @throws HttpException if the requested range cannot be satisfied. * @throws HttpException if the requested range cannot be satisfied.
*/ */
public function sendStreamAsFile($handle, $attachmentName, $mimeType = 'application/octet-stream') public function sendStreamAsFile($handle, $attachmentName, $options = [])
{ {
$headers = $this->getHeaders(); $headers = $this->getHeaders();
fseek($handle, 0, SEEK_END); fseek($handle, 0, SEEK_END);
...@@ -513,7 +530,8 @@ class Response extends \yii\base\Response ...@@ -513,7 +530,8 @@ class Response extends \yii\base\Response
$this->setStatusCode(200); $this->setStatusCode(200);
} }
$this->setDownloadHeaders($attachmentName, $mimeType, $end - $begin + 1); $mimeType = isset($options['mimeType']) ? $options['mimeType'] : 'application/octet-stream';
$this->setDownloadHeaders($attachmentName, $mimeType, !empty($options['inline']), $end - $begin + 1);
$this->format = self::FORMAT_RAW; $this->format = self::FORMAT_RAW;
$this->stream = [$handle, $begin, $end]; $this->stream = [$handle, $begin, $end];
...@@ -525,19 +543,22 @@ class Response extends \yii\base\Response ...@@ -525,19 +543,22 @@ class Response extends \yii\base\Response
* Sets a default set of HTTP headers for file downloading purpose. * Sets a default set of HTTP headers for file downloading purpose.
* @param string $attachmentName the attachment file name * @param string $attachmentName the attachment file name
* @param string $mimeType the MIME type for the response. If null, `Content-Type` header will NOT be set. * @param string $mimeType the MIME type for the response. If null, `Content-Type` header will NOT be set.
* @param boolean $inline whether the browser should open the file within the browser window. Defaults to false,
* meaning a download dialog will pop up.
* @param integer $contentLength the byte length of the file being downloaded. If null, `Content-Length` header will NOT be set. * @param integer $contentLength the byte length of the file being downloaded. If null, `Content-Length` header will NOT be set.
* @return static the response object itself * @return static the response object itself
*/ */
public function setDownloadHeaders($attachmentName, $mimeType = null, $contentLength = null) public function setDownloadHeaders($attachmentName, $mimeType = null, $inline = false, $contentLength = null)
{ {
$headers = $this->getHeaders(); $headers = $this->getHeaders();
$disposition = $inline ? 'attachment' : 'inline';
$headers->setDefault('Pragma', 'public') $headers->setDefault('Pragma', 'public')
->setDefault('Accept-Ranges', 'bytes') ->setDefault('Accept-Ranges', 'bytes')
->setDefault('Expires', '0') ->setDefault('Expires', '0')
->setDefault('Cache-Control', 'must-revalidate, post-check=0, pre-check=0') ->setDefault('Cache-Control', 'must-revalidate, post-check=0, pre-check=0')
->setDefault('Content-Transfer-Encoding', 'binary') ->setDefault('Content-Transfer-Encoding', 'binary')
->setDefault('Content-Disposition', "attachment; filename=\"$attachmentName\""); ->setDefault('Content-Disposition', "$disposition; filename=\"$attachmentName\"");
if ($mimeType !== null) { if ($mimeType !== null) {
$headers->setDefault('Content-Type', $mimeType); $headers->setDefault('Content-Type', $mimeType);
...@@ -631,31 +652,32 @@ class Response extends \yii\base\Response ...@@ -631,31 +652,32 @@ class Response extends \yii\base\Response
* *
* @param string $filePath file name with full path * @param string $filePath file name with full path
* @param string $attachmentName file name shown to the user. If null, it will be determined from `$filePath`. * @param string $attachmentName file name shown to the user. If null, it will be determined from `$filePath`.
* @param string $mimeType the MIME type of the file. If null, it will be determined based on `$filePath`. * @param array $options additional options for sending the file. The following options are supported:
* @param array $options additional options. Valid options are: *
* - forceDownload: boolean, whether the file will be downloaded or shown inline. Defaults to true. * - `mimeType`: the MIME type of the content. If not set, it will be guessed based on `$filePath`
* - xHeader: string, the name of the x-sendfile header. Defaults to "X-Sendfile". * - `inline`: boolean, whether the browser should open the file within the browser window. Defaults to false,
* meaning a download dialog will pop up.
* - xHeader: string, the name of the x-sendfile header. Defaults to "X-Sendfile".
*
* @return static the response object itself * @return static the response object itself
*/ */
public function xSendFile($filePath, $attachmentName = null, $mimeType = null, $options = []) public function xSendFile($filePath, $attachmentName = null, $options = [])
{ {
if ($mimeType === null && ($mimeType = FileHelper::getMimeTypeByExtension($filePath)) === null) {
$mimeType = 'application/octet-stream';
}
if ($attachmentName === null) { if ($attachmentName === null) {
$attachmentName = basename($filePath); $attachmentName = basename($filePath);
} }
if (isset($options['mimeType'])) {
$mimeType = $options['mimeType'];
} elseif (($mimeType = FileHelper::getMimeTypeByExtension($filePath)) === null) {
$mimeType = 'application/octet-stream';
}
if (isset($options['xHeader'])) { if (isset($options['xHeader'])) {
$xHeader = $options['xHeader']; $xHeader = $options['xHeader'];
} else { } else {
$xHeader = 'X-Sendfile'; $xHeader = 'X-Sendfile';
} }
if (!isset($options['forceDownload']) || $options['forceDownload']) {
$disposition = 'attachment';
} else {
$disposition = 'inline';
}
$disposition = empty($options['inline']) ? 'attachment' : 'inline';
$this->getHeaders() $this->getHeaders()
->setDefault($xHeader, $filePath) ->setDefault($xHeader, $filePath)
->setDefault('Content-Type', $mimeType) ->setDefault('Content-Type', $mimeType)
......
...@@ -10,6 +10,11 @@ use yii\helpers\StringHelper; ...@@ -10,6 +10,11 @@ use yii\helpers\StringHelper;
*/ */
class ResponseTest extends \yiiunit\TestCase class ResponseTest extends \yiiunit\TestCase
{ {
/**
* @var \yii\web\Response
*/
public $response;
protected function setUp() protected function setUp()
{ {
parent::setUp(); parent::setUp();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment