Deprecated MediaView and updated MediaView::render() to use CakeResponse::file()

This commit is contained in:
ADmad 2012-07-11 01:33:40 +05:30
parent c5d1260421
commit 69eba678f4
2 changed files with 86 additions and 281 deletions

View file

@ -35,8 +35,15 @@ class MediaViewTest extends CakeTestCase {
*/ */
public function setUp() { public function setUp() {
parent::setUp(); parent::setUp();
$this->MediaView = $this->getMock('MediaView', array('_isActive', '_clearBuffer', '_flushBuffer')); $this->MediaView = new MediaView();
$this->MediaView->response = $this->getMock('CakeResponse'); $this->MediaView->response = $this->getMock('CakeResponse', array(
'_isActive',
'_clearBuffer',
'_flushBuffer',
'type',
'header',
'download'
));
} }
/** /**
@ -71,10 +78,10 @@ class MediaViewTest extends CakeTestCase {
public function testRender() { public function testRender() {
$this->MediaView->viewVars = array( $this->MediaView->viewVars = array(
'path' => CAKE . 'Test' . DS . 'test_app' . DS . 'Vendor' . DS . 'css' . DS, 'path' => CAKE . 'Test' . DS . 'test_app' . DS . 'Vendor' . DS . 'css' . DS,
'id' => 'test_asset.css', 'id' => 'test_asset.css'
'extension' => 'css',
); );
$this->MediaView->expects($this->exactly(2))
$this->MediaView->response->expects($this->exactly(1))
->method('_isActive') ->method('_isActive')
->will($this->returnValue(true)); ->will($this->returnValue(true));
@ -83,23 +90,23 @@ class MediaViewTest extends CakeTestCase {
->with('css') ->with('css')
->will($this->returnArgument(0)); ->will($this->returnArgument(0));
$this->MediaView->response->expects($this->at(1)) $this->MediaView->response->expects($this->at(0))
->method('header') ->method('header')
->with(array( ->with(array(
'Date' => gmdate('D, d M Y H:i:s', time()) . ' GMT', 'Expires' => 'Mon, 26 Jul 1997 05:00:00 GMT',
'Expires' => '0', 'Cache-Control' => 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0',
'Cache-Control' => 'private, must-revalidate, post-check=0, pre-check=0', 'Last-Modified' => gmdate('D, d M Y H:i:s', time()) . ' GMT'
'Pragma' => 'no-cache'
)); ));
$this->MediaView->response->expects($this->at(2)) $this->MediaView->response->expects($this->at(2))
->method('header') ->method('header')
->with(array( ->with('Content-Length', 31);
'Content-Length' => 31
)); $this->MediaView->response->expects($this->once())->method('_clearBuffer');
$this->MediaView->response->expects($this->once())->method('send'); $this->MediaView->response->expects($this->exactly(1))
$this->MediaView->expects($this->once())->method('_clearBuffer'); ->method('_isActive')
$this->MediaView->expects($this->once())->method('_flushBuffer'); ->will($this->returnValue(true));
$this->MediaView->response->expects($this->once())->method('_flushBuffer');
ob_start(); ob_start();
$result = $this->MediaView->render(); $result = $this->MediaView->render();
@ -118,25 +125,20 @@ class MediaViewTest extends CakeTestCase {
$_SERVER['HTTP_USER_AGENT'] = 'Some generic browser'; $_SERVER['HTTP_USER_AGENT'] = 'Some generic browser';
$this->MediaView->viewVars = array( $this->MediaView->viewVars = array(
'path' => CAKE . 'Test' . DS . 'test_app' . DS . 'Config' . DS, 'path' => CAKE . 'Test' . DS . 'test_app' . DS . 'Config' . DS,
'id' => 'no_section.ini', 'id' => 'no_section.ini'
'extension' => 'ini',
); );
$this->MediaView->expects($this->exactly(2))
->method('_isActive')
->will($this->returnValue(true));
$this->MediaView->response->expects($this->exactly(1)) $this->MediaView->response->expects($this->exactly(1))
->method('type') ->method('type')
->with('ini') ->with('ini')
->will($this->returnValue(false)); ->will($this->returnValue(false));
$this->MediaView->response->expects($this->at(1)) $this->MediaView->response->expects($this->at(0))
->method('header') ->method('header')
->with(array( ->with(array(
'Date' => gmdate('D, d M Y H:i:s', time()) . ' GMT', 'Expires' => 'Mon, 26 Jul 1997 05:00:00 GMT',
'Expires' => '0', 'Cache-Control' => 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0',
'Cache-Control' => 'private, must-revalidate, post-check=0, pre-check=0', 'Last-Modified' => gmdate('D, d M Y H:i:s', time()) . ' GMT'
'Pragma' => 'no-cache'
)); ));
$this->MediaView->response->expects($this->once()) $this->MediaView->response->expects($this->once())
@ -145,17 +147,17 @@ class MediaViewTest extends CakeTestCase {
$this->MediaView->response->expects($this->at(3)) $this->MediaView->response->expects($this->at(3))
->method('header') ->method('header')
->with(array( ->with('Accept-Ranges', 'bytes');
'Accept-Ranges' => 'bytes'
));
$this->MediaView->response->expects($this->at(4)) $this->MediaView->response->expects($this->at(4))
->method('header') ->method('header')
->with('Content-Length', 35); ->with('Content-Length', 35);
$this->MediaView->response->expects($this->once())->method('send'); $this->MediaView->response->expects($this->once())->method('_clearBuffer');
$this->MediaView->expects($this->once())->method('_clearBuffer'); $this->MediaView->response->expects($this->exactly(1))
$this->MediaView->expects($this->once())->method('_flushBuffer'); ->method('_isActive')
->will($this->returnValue(true));
$this->MediaView->response->expects($this->once())->method('_flushBuffer');
ob_start(); ob_start();
$result = $this->MediaView->render(); $result = $this->MediaView->render();
@ -178,48 +180,43 @@ class MediaViewTest extends CakeTestCase {
$this->MediaView->viewVars = array( $this->MediaView->viewVars = array(
'path' => CAKE . 'Test' . DS . 'test_app' . DS . 'Config' . DS, 'path' => CAKE . 'Test' . DS . 'test_app' . DS . 'Config' . DS,
'id' => 'no_section.ini', 'id' => 'no_section.ini',
'extension' => 'ini',
); );
$this->MediaView->expects($this->exactly(2))
->method('_isActive')
->will($this->returnValue(true));
$this->MediaView->response->expects($this->at(0)) $this->MediaView->response->expects($this->at(0))
->method('header')
->with(array(
'Expires' => 'Mon, 26 Jul 1997 05:00:00 GMT',
'Cache-Control' => 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0',
'Last-Modified' => gmdate('D, d M Y H:i:s', time()) . ' GMT'
));
$this->MediaView->response->expects($this->at(1))
->method('type') ->method('type')
->with('ini') ->with('ini')
->will($this->returnValue(false)); ->will($this->returnValue(false));
$this->MediaView->response->expects($this->at(1))
->method('header')
->with(array(
'Date' => gmdate('D, d M Y H:i:s', time()) . ' GMT',
'Expires' => '0',
'Cache-Control' => 'private, must-revalidate, post-check=0, pre-check=0',
'Pragma' => 'no-cache'
));
$this->MediaView->response->expects($this->at(2)) $this->MediaView->response->expects($this->at(2))
->method('type') ->method('type')
->with('application/octetstream') ->with('application/octetstream')
->will($this->returnValue(false)); ->will($this->returnValue(false));
$this->MediaView->response->expects($this->once()) $this->MediaView->response->expects($this->at(3))
->method('download') ->method('download')
->with('no_section.ini'); ->with('no_section.ini');
$this->MediaView->response->expects($this->at(4)) $this->MediaView->response->expects($this->at(4))
->method('header') ->method('header')
->with(array( ->with('Accept-Ranges', 'bytes');
'Accept-Ranges' => 'bytes'
));
$this->MediaView->response->expects($this->at(5)) $this->MediaView->response->expects($this->at(5))
->method('header') ->method('header')
->with('Content-Length', 35); ->with('Content-Length', 35);
$this->MediaView->response->expects($this->once())->method('send'); $this->MediaView->response->expects($this->once())->method('_clearBuffer');
$this->MediaView->expects($this->once())->method('_clearBuffer'); $this->MediaView->response->expects($this->exactly(1))
$this->MediaView->expects($this->once())->method('_flushBuffer'); ->method('_isActive')
->will($this->returnValue(true));
$this->MediaView->response->expects($this->once())->method('_flushBuffer');
ob_start(); ob_start();
$result = $this->MediaView->render(); $result = $this->MediaView->render();
@ -242,49 +239,44 @@ class MediaViewTest extends CakeTestCase {
$this->MediaView->viewVars = array( $this->MediaView->viewVars = array(
'path' => CAKE . 'Test' . DS . 'test_app' . DS . 'Config' . DS, 'path' => CAKE . 'Test' . DS . 'test_app' . DS . 'Config' . DS,
'id' => 'no_section.ini', 'id' => 'no_section.ini',
'extension' => 'ini',
'name' => 'config' 'name' => 'config'
); );
$this->MediaView->expects($this->exactly(2))
->method('_isActive')
->will($this->returnValue(true));
$this->MediaView->response->expects($this->at(0)) $this->MediaView->response->expects($this->at(0))
->method('header')
->with(array(
'Expires' => 'Mon, 26 Jul 1997 05:00:00 GMT',
'Cache-Control' => 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0',
'Last-Modified' => gmdate('D, d M Y H:i:s', time()) . ' GMT'
));
$this->MediaView->response->expects($this->at(1))
->method('type') ->method('type')
->with('ini') ->with('ini')
->will($this->returnValue(false)); ->will($this->returnValue(false));
$this->MediaView->response->expects($this->at(1))
->method('header')
->with(array(
'Date' => gmdate('D, d M Y H:i:s', time()) . ' GMT',
'Expires' => '0',
'Cache-Control' => 'private, must-revalidate, post-check=0, pre-check=0',
'Pragma' => 'no-cache'
));
$this->MediaView->response->expects($this->at(2)) $this->MediaView->response->expects($this->at(2))
->method('type') ->method('type')
->with('application/force-download') ->with('application/force-download')
->will($this->returnValue(false)); ->will($this->returnValue(false));
$this->MediaView->response->expects($this->once()) $this->MediaView->response->expects($this->at(3))
->method('download') ->method('download')
->with('config.ini'); ->with('config.ini');
$this->MediaView->response->expects($this->at(4)) $this->MediaView->response->expects($this->at(4))
->method('header') ->method('header')
->with(array( ->with('Accept-Ranges', 'bytes');
'Accept-Ranges' => 'bytes'
));
$this->MediaView->response->expects($this->at(5)) $this->MediaView->response->expects($this->at(5))
->method('header') ->method('header')
->with('Content-Length', 35); ->with('Content-Length', 35);
$this->MediaView->response->expects($this->once())->method('send'); $this->MediaView->response->expects($this->once())->method('_clearBuffer');
$this->MediaView->expects($this->once())->method('_clearBuffer'); $this->MediaView->response->expects($this->exactly(1))
$this->MediaView->expects($this->once())->method('_flushBuffer'); ->method('_isActive')
->will($this->returnValue(true));
$this->MediaView->response->expects($this->once())->method('_flushBuffer');
ob_start(); ob_start();
$result = $this->MediaView->render(); $result = $this->MediaView->render();
@ -296,29 +288,6 @@ class MediaViewTest extends CakeTestCase {
} }
} }
/**
* testConnectionAborted method
*
* @return void
*/
public function testConnectionAborted() {
$this->MediaView->viewVars = array(
'path' => CAKE . 'Test' . DS . 'test_app' . DS . 'Vendor' . DS . 'css' . DS,
'id' => 'test_asset.css',
'extension' => 'css',
);
$this->MediaView->expects($this->once())
->method('_isActive')
->will($this->returnValue(false));
$this->MediaView->response->expects($this->never())
->method('type');
$result = $this->MediaView->render();
$this->assertFalse($result);
}
/** /**
* testConnectionAbortedOnBuffering method * testConnectionAbortedOnBuffering method
* *
@ -327,29 +296,22 @@ class MediaViewTest extends CakeTestCase {
public function testConnectionAbortedOnBuffering() { public function testConnectionAbortedOnBuffering() {
$this->MediaView->viewVars = array( $this->MediaView->viewVars = array(
'path' => CAKE . 'Test' . DS . 'test_app' . DS . 'Vendor' . DS . 'css' . DS, 'path' => CAKE . 'Test' . DS . 'test_app' . DS . 'Vendor' . DS . 'css' . DS,
'id' => 'test_asset.css', 'id' => 'test_asset.css'
'extension' => 'css',
); );
$this->MediaView->expects($this->at(0))
->method('_isActive')
->will($this->returnValue(true));
$this->MediaView->response->expects($this->any()) $this->MediaView->response->expects($this->any())
->method('type') ->method('type')
->with('css') ->with('css')
->will($this->returnArgument(0)); ->will($this->returnArgument(0));
$this->MediaView->expects($this->at(1)) $this->MediaView->response->expects($this->at(1))
->method('_isActive') ->method('_isActive')
->will($this->returnValue(false)); ->will($this->returnValue(false));
$this->MediaView->response->expects($this->once())->method('send'); $this->MediaView->response->expects($this->once())->method('_clearBuffer');
$this->MediaView->expects($this->once())->method('_clearBuffer'); $this->MediaView->response->expects($this->never())->method('_flushBuffer');
$this->MediaView->expects($this->never())->method('_flushBuffer');
$result = $this->MediaView->render(); $this->MediaView->render();
$this->assertFalse($result);
} }
/** /**
@ -360,8 +322,7 @@ class MediaViewTest extends CakeTestCase {
public function testRenderUpperExtension() { public function testRenderUpperExtension() {
$this->MediaView->viewVars = array( $this->MediaView->viewVars = array(
'path' => CAKE . 'Test' . DS . 'test_app' . DS . 'Vendor' . DS . 'img' . DS, 'path' => CAKE . 'Test' . DS . 'test_app' . DS . 'Vendor' . DS . 'img' . DS,
'id' => 'test_2.JPG', 'id' => 'test_2.JPG'
'extension' => 'JPG',
); );
$this->MediaView->response->expects($this->any()) $this->MediaView->response->expects($this->any())
@ -369,30 +330,7 @@ class MediaViewTest extends CakeTestCase {
->with('jpg') ->with('jpg')
->will($this->returnArgument(0)); ->will($this->returnArgument(0));
$this->MediaView->expects($this->at(0)) $this->MediaView->response->expects($this->at(0))
->method('_isActive')
->will($this->returnValue(true));
$this->MediaView->render();
}
/**
* Test downloading files with extension not explicitly set.
*
* @return void
*/
public function testRenderExtensionNotSet() {
$this->MediaView->viewVars = array(
'path' => CAKE . 'Test' . DS . 'test_app' . DS . 'Vendor' . DS . 'img' . DS,
'id' => 'test_2.JPG',
);
$this->MediaView->response->expects($this->any())
->method('type')
->with('jpg')
->will($this->returnArgument(0));
$this->MediaView->expects($this->at(0))
->method('_isActive') ->method('_isActive')
->will($this->returnValue(true)); ->will($this->returnValue(true));

View file

@ -15,6 +15,7 @@
* @package Cake.View * @package Cake.View
* @since CakePHP(tm) v 1.2.0.5714 * @since CakePHP(tm) v 1.2.0.5714
* @license MIT License (http://www.opensource.org/licenses/mit-license.php) * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
* @deprecated Deprecated since version 2.3, use CakeResponse::file() instead
*/ */
App::uses('View', 'View'); App::uses('View', 'View');
@ -31,8 +32,6 @@ App::uses('CakeRequest', 'Network');
* - `id` The filename on the server's filesystem, including extension. * - `id` The filename on the server's filesystem, including extension.
* - `name` The filename that will be sent to the user, specified without the extension. * - `name` The filename that will be sent to the user, specified without the extension.
* - `download` Set to true to set a `Content-Disposition` header. This is ideal for file downloads. * - `download` Set to true to set a `Content-Disposition` header. This is ideal for file downloads.
* - `extension` The extension of the file being served. This is used to set the mimetype.
* If not provided its extracted from filename provided as `id`.
* - `path` The absolute path, including the trailing / on the server's filesystem to `id`. * - `path` The absolute path, including the trailing / on the server's filesystem to `id`.
* - `mimeType` The mime type of the file if CakeResponse doesn't know about it. * - `mimeType` The mime type of the file if CakeResponse doesn't know about it.
* Must be an associative array with extension as key and mime type as value eg. array('ini' => 'text/plain') * Must be an associative array with extension as key and mime type as value eg. array('ini' => 'text/plain')
@ -59,23 +58,15 @@ App::uses('CakeRequest', 'Network');
*/ */
class MediaView extends View { class MediaView extends View {
/**
* Indicates whether response gzip compression was enabled for this class
*
* @var boolean
*/
protected $_compressionEnabled = false;
/** /**
* Display or download the given file * Display or download the given file
* *
* @param string $view Not used * @param string $view Not used
* @param string $layout Not used * @param string $layout Not used
* @return mixed * @return boolean
* @throws NotFoundException
*/ */
public function render($view = null, $layout = null) { public function render($view = null, $layout = null) {
$name = $download = $extension = $id = $modified = $path = $cache = $mimeType = $compress = null; $name = $download = $id = $modified = $path = $cache = $mimeType = $compress = null;
extract($this->viewVars, EXTR_OVERWRITE); extract($this->viewVars, EXTR_OVERWRITE);
if (is_dir($path)) { if (is_dir($path)) {
@ -84,155 +75,31 @@ class MediaView extends View {
$path = APP . $path . $id; $path = APP . $path . $id;
} }
if (!is_file($path)) {
if (Configure::read('debug')) {
throw new NotFoundException(sprintf('The requested file %s was not found', $path));
}
throw new NotFoundException('The requested file was not found');
}
if (is_array($mimeType)) { if (is_array($mimeType)) {
$this->response->type($mimeType); $this->response->type($mimeType);
} }
if (!isset($extension)) { if ($cache) {
$extension = pathinfo($id, PATHINFO_EXTENSION);
}
if ($this->_isActive()) {
$extension = strtolower($extension);
$chunkSize = 8192;
$buffer = '';
$fileSize = @filesize($path);
$handle = fopen($path, 'rb');
if ($handle === false) {
return false;
}
if (!empty($modified) && !is_numeric($modified)) { if (!empty($modified) && !is_numeric($modified)) {
$modified = strtotime($modified, time()); $modified = strtotime($modified, time());
} else { } else {
$modified = time(); $modified = time();
} }
if (!$extension || $this->response->type($extension) === false) {
$download = true;
}
if ($cache) {
$this->response->cache($modified, $cache); $this->response->cache($modified, $cache);
} else { } else {
$this->response->header(array( $this->response->disableCache();
'Date' => gmdate('D, d M Y H:i:s', time()) . ' GMT',
'Expires' => '0',
'Cache-Control' => 'private, must-revalidate, post-check=0, pre-check=0',
'Pragma' => 'no-cache'
));
} }
if ($download) { if ($name !== null) {
$agent = env('HTTP_USER_AGENT'); $name .= '.' . pathinfo($id, PATHINFO_EXTENSION);
if (preg_match('%Opera(/| )([0-9].[0-9]{1,2})%', $agent)) {
$contentType = 'application/octetstream';
} elseif (preg_match('/MSIE ([0-9].[0-9]{1,2})/', $agent)) {
$contentType = 'application/force-download';
} }
$this->response->file($path, compact('name', 'download'));
if (!empty($contentType)) {
$this->response->type($contentType);
}
if (is_null($name)) {
$name = $id;
} elseif ($extension) {
$name .= '.' . $extension;
}
$this->response->download($name);
$this->response->header(array('Accept-Ranges' => 'bytes'));
$httpRange = env('HTTP_RANGE');
if (isset($httpRange)) {
list($toss, $range) = explode('=', $httpRange);
$size = $fileSize - 1;
$length = $fileSize - $range;
$this->response->header(array(
'Content-Length' => $length,
'Content-Range' => 'bytes ' . $range . $size . '/' . $fileSize
));
$this->response->statusCode(206);
fseek($handle, $range);
} else {
$this->response->header('Content-Length', $fileSize);
}
} else {
$this->response->header(array(
'Content-Length' => $fileSize
));
}
$this->_clearBuffer();
if ($compress) { if ($compress) {
$this->_compressionEnabled = $this->response->compress(); $this->response->compress();
} }
$this->response->send(); $this->response->send();
return $this->_sendFile($handle); return true;
}
return false;
}
/**
* Reads out a file handle, and echos the content to the client.
*
* @param resource $handle A file handle or stream
* @return void
*/
protected function _sendFile($handle) {
$chunkSize = 8192;
$buffer = '';
while (!feof($handle)) {
if (!$this->_isActive()) {
fclose($handle);
return false;
}
set_time_limit(0);
$buffer = fread($handle, $chunkSize);
echo $buffer;
if (!$this->_compressionEnabled) {
$this->_flushBuffer();
}
}
fclose($handle);
}
/**
* Returns true if connection is still active
*
* @return boolean
*/
protected function _isActive() {
return connection_status() == 0 && !connection_aborted();
}
/**
* Clears the contents of the topmost output buffer and discards them
*
* @return boolean
*/
protected function _clearBuffer() {
return @ob_end_clean();
}
/**
* Flushes the contents of the output buffer
*
* @return void
*/
protected function _flushBuffer() {
@flush();
@ob_flush();
} }
} }