Skip to content

Commit

Permalink
feature #18502 [FrameworkBundle] Add file helper to Controller (dfrid…
Browse files Browse the repository at this point in the history
…rich)

This PR was squashed before being merged into the 3.2-dev branch (closes #18502).

Discussion
----------

[FrameworkBundle] Add file helper to Controller

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets |
| License       | MIT
| Doc PR        | symfony/symfony-docs#6454

I think it would be more "sexy" to serve files from controller easier (like `json()` helper does).

**This Controller helper allows user to serve files to Response in these ways:**

* pass `Symfony\Component\HttpFoundation\File` (or `Symfony\Component\HttpFoundation\UploadedFile`) instance
* [REMOVED] provide content as `string` and specify file name (mime type will be auto recognized)
* provide path to file (you are still able to specify other than original file name)

**Examples**

    return $this->file($uploadedFile);
    // ...or...
    return $this->file('/path/to/my/picture.jpg');

Commits
-------

d9a8499 [FrameworkBundle] Add file helper to Controller
  • Loading branch information
fabpot committed Jun 15, 2016
2 parents 161d629 + d9a8499 commit 1e263c0
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 0 deletions.
20 changes: 20 additions & 0 deletions src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php
Expand Up @@ -13,9 +13,12 @@

use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\HttpKernelInterface;
Expand Down Expand Up @@ -123,6 +126,23 @@ protected function json($data, $status = 200, $headers = array(), $context = arr
return new JsonResponse($data, $status, $headers);
}

/**
* Returns a BinaryFileResponse object with original or customized file name and disposition header.
*
* @param File|string $file File object or path to file to be sent as response
* @param string|null $fileName File name to be sent to response or null (will use original file name)
* @param string $disposition Disposition of response ("attachment" is default, other type is "inline")
*
* @return BinaryFileResponse
*/
protected function file($file, $fileName = null, $disposition = ResponseHeaderBag::DISPOSITION_ATTACHMENT)
{
$response = new BinaryFileResponse($file);
$response->setContentDisposition($disposition, $fileName === null ? $response->getFile()->getFileName() : $fileName);

return $response;
}

/**
* Adds a flash message to the current session for type.
*
Expand Down
128 changes: 128 additions & 0 deletions src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTest.php
Expand Up @@ -14,10 +14,13 @@
use Symfony\Bundle\FrameworkBundle\Tests\TestCase;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
Expand Down Expand Up @@ -209,6 +212,126 @@ public function testJsonWithSerializerContextOverride()
$this->assertEquals('{}', $response->getContent());
}

public function testFile()
{
/* @var ContainerInterface $container */
$container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface');
$kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface');
$container->set('kernel', $kernel);

$controller = new TestController();
$controller->setContainer($container);

/* @var BinaryFileResponse $response */
$response = $controller->file(new File(__FILE__));
$this->assertInstanceOf(BinaryFileResponse::class, $response);
$this->assertSame(200, $response->getStatusCode());
if ($response->headers->get('content-type')) {
$this->assertSame('text/x-php', $response->headers->get('content-type'));
}
$this->assertContains(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $response->headers->get('content-disposition'));
$this->assertContains(basename(__FILE__), $response->headers->get('content-disposition'));
}

public function testFileAsInline()
{
$container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface');
$controller = new TestController();
$controller->setContainer($container);

/* @var BinaryFileResponse $response */
$response = $controller->file(new File(__FILE__), null, ResponseHeaderBag::DISPOSITION_INLINE);

$this->assertInstanceOf(BinaryFileResponse::class, $response);
$this->assertSame(200, $response->getStatusCode());
if ($response->headers->get('content-type')) {
$this->assertSame('text/x-php', $response->headers->get('content-type'));
}
$this->assertContains(ResponseHeaderBag::DISPOSITION_INLINE, $response->headers->get('content-disposition'));
$this->assertContains(basename(__FILE__), $response->headers->get('content-disposition'));
}

public function testFileWithOwnFileName()
{
$container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface');
$controller = new TestController();
$controller->setContainer($container);

/* @var BinaryFileResponse $response */
$fileName = 'test.php';
$response = $controller->file(new File(__FILE__), $fileName);

$this->assertInstanceOf(BinaryFileResponse::class, $response);
$this->assertSame(200, $response->getStatusCode());
if ($response->headers->get('content-type')) {
$this->assertSame('text/x-php', $response->headers->get('content-type'));
}
$this->assertContains(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $response->headers->get('content-disposition'));
$this->assertContains($fileName, $response->headers->get('content-disposition'));
}

public function testFileWithOwnFileNameAsInline()
{
$container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface');
$controller = new TestController();
$controller->setContainer($container);

/* @var BinaryFileResponse $response */
$fileName = 'test.php';
$response = $controller->file(new File(__FILE__), $fileName, ResponseHeaderBag::DISPOSITION_INLINE);

$this->assertInstanceOf(BinaryFileResponse::class, $response);
$this->assertSame(200, $response->getStatusCode());
if ($response->headers->get('content-type')) {
$this->assertSame('text/x-php', $response->headers->get('content-type'));
}
$this->assertContains(ResponseHeaderBag::DISPOSITION_INLINE, $response->headers->get('content-disposition'));
$this->assertContains($fileName, $response->headers->get('content-disposition'));
}

public function testFileFromPath()
{
$controller = new TestController();

/* @var BinaryFileResponse $response */
$response = $controller->file(__FILE__);

$this->assertInstanceOf(BinaryFileResponse::class, $response);
$this->assertSame(200, $response->getStatusCode());
if ($response->headers->get('content-type')) {
$this->assertSame('text/x-php', $response->headers->get('content-type'));
}
$this->assertContains(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $response->headers->get('content-disposition'));
$this->assertContains(basename(__FILE__), $response->headers->get('content-disposition'));
}

public function testFileFromPathWithCustomizedFileName()
{
$controller = new TestController();

/* @var BinaryFileResponse $response */
$response = $controller->file(__FILE__, 'test.php');

$this->assertInstanceOf(BinaryFileResponse::class, $response);
$this->assertSame(200, $response->getStatusCode());
if ($response->headers->get('content-type')) {
$this->assertSame('text/x-php', $response->headers->get('content-type'));
}
$this->assertContains(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $response->headers->get('content-disposition'));
$this->assertContains('test.php', $response->headers->get('content-disposition'));
}

/**
* @expectedException \Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException
*/
public function testFileWhichDoesNotExist()
{
$controller = new TestController();

/* @var BinaryFileResponse $response */
$response = $controller->file('some-file.txt', 'test.php');
}

public function testIsGranted()
{
$authorizationChecker = $this->getMock('Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface');
Expand Down Expand Up @@ -494,6 +617,11 @@ public function json($data, $status = 200, $headers = array(), $context = array(
return parent::json($data, $status, $headers, $context);
}

public function file($file, $fileName = null, $disposition = ResponseHeaderBag::DISPOSITION_ATTACHMENT)
{
return parent::file($file, $fileName, $disposition);
}

public function isGranted($attributes, $object = null)
{
return parent::isGranted($attributes, $object);
Expand Down

0 comments on commit 1e263c0

Please sign in to comment.