Skip to content

[FrameworkBundle] Add file helper to Controller #18502

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 24 commits into from

Conversation

dfridrich
Copy link
Contributor

@dfridrich dfridrich commented Apr 10, 2016

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');

@javiereguiluz
Copy link
Member

@dfridrich thanks for sending this contribution to improve Symfony (and its associated docs too).

Just a comment for your next contributions: it's always better to open an issue to propose new features without sending the PR to implement them. That allows the community to review your idea and propose changes to it. Besides, if the idea is ultimately rejected, creating a PR will make you waste your time whereas creating an issue is quick and easy. Thanks!

@dfridrich
Copy link
Contributor Author

@javiereguiluz thanks for information, I'll remember next time.

$mimeType = $file->getMimeType();
} elseif (is_string($file)) {
if ($mimeType === null) {
throw new \InvalidArgumentException('You must specify mime type for file from string.');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could use the MimeTypeGuesser here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Nicofuma thx, MimeTypeGuesser is now implemented (using BinaryFileResponse class)

@dfridrich
Copy link
Contributor Author

dfridrich commented May 2, 2016

I think this my PR is really very useful, what do you think @symfony/deciders?

* Returns a BinaryFileResponse object with original or customized file name and disposition header.
*
* @param File|string $file File object, path to file or string with content to be sent as response
* @param null $fileName File name to be sent to response or null (will use original file name)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be @param string|null

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@HeahDude you are right, thx

@HeahDude
Copy link
Contributor

HeahDude commented May 2, 2016

Besides my minor comments, I think this is a nice addition. Thanks :)

@dfridrich
Copy link
Contributor Author

@HeahDude Thanks for your feedback, I really appreciate it! I just commited fixed version of this great feature 👻

throw new \InvalidArgumentException(
sprintf(
'"%s" can\'t be passed as parameter to "%s" method of "%s" class.',
is_object($file) ? 'Instance of '.get_class($file) : gettype($file),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've no strong opinion about this either, it just seems to me (IIUC) that at this point $file as string has been transformed to a File object, so it means if you got something else and you cannot be sure it's a scalar.
One other way would be to throw an \InvalidArgumentException when not string or File at the very beginning of the method.

@jvasseur
Copy link
Contributor

jvasseur commented May 2, 2016

I don't think allowing to pass a string containing the file contents is a good idea. If you already have the content of the response, why storing it in a temporary file to then read back from it ?

Plus this means passing a string as a first argument can mean two totally different things, which means if for example you make a typo in the name of the file you want to send you will get this name as a file instead of an error.

@xabbuh
Copy link
Member

xabbuh commented May 2, 2016

I agree with @jvasseur. We had something similar in the YAML parser in the past which was not a good idea (we cleared things in 3.0). We should simply not support passing file contents here.


$response->headers->set('Content-Type', $mimeType);
$disposition = $response->headers->makeDisposition($disposition, $fileName === null ? $file->getFileName() : $fileName);
$response->headers->set('Content-Disposition', $disposition);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be better to use the setContentDisposition() of BinaryFileResponse:

$response->setContentDisposition($disposition, $fileName === null ? $file->getFileName() : $fileName);

$file = new File($file);
}

$response = new BinaryFileResponse($file, 200, ['Content-type' => $file->getMimeType()]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I commented after your changes, the content-type logic is already done in BinaryFileResponse, so this can be removed.


$response = new BinaryFileResponse($file, 200, ['Content-type' => $file->getMimeType()]);
$response->setContentDisposition($disposition, $fileName === null ? $file->getFileName() : $fileName);
$response->headers->set('Content-Disposition', $disposition);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line should be removed.

@fabpot
Copy link
Member

fabpot commented Jun 15, 2016

The method should read as follows:

    protected function file($file, $fileName = null, $disposition = ResponseHeaderBag::DISPOSITION_ATTACHMENT)
    {
        $response = new BinaryFileResponse($file);
        $response->setContentDisposition($disposition, null === $fileName ? $file->getFileName() : $fileName);

        return $response;
    }

@jvasseur
Copy link
Contributor

@fabpot your version doesn't work if $file is a string and $fileName is null.

Maybe something like this:

    protected function file($file, $fileName = '', $disposition = ResponseHeaderBag::DISPOSITION_ATTACHMENT)
    {
        $response = new BinaryFileResponse($file);
        $response->setContentDisposition($disposition, $fileName);

        return $response;
    }

$this->assertSame('text/x-php', $response->headers->get('content-type'));
}
$this->assertContains(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $response->headers->get('content-disposition'));
$this->assertContains(pathinfo(__FILE__, PATHINFO_BASENAME), $response->headers->get('content-disposition'));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function basename is simpler than pathinfofor such thing.

@fabpot
Copy link
Member

fabpot commented Jun 15, 2016

Thank you @dfridrich.

@fabpot fabpot closed this in 1e263c0 Jun 15, 2016
@@ -124,6 +127,23 @@ protected function json($data, $status = 200, $headers = array(), $context = arr
}

/**
* 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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be \SplFileInfo|string to be equal to BinaryFileResponse?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch, see #19115

fabpot added a commit that referenced this pull request Jun 20, 2016
This PR was merged into the 3.2-dev branch.

Discussion
----------

[FrameworkBundle] fix argument type docblock

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #18502 (comment)
| License       | MIT
| Doc PR        |

Commits
-------

c4e440c [FrameworkBundle] fix argument type docblock
*
* @return BinaryFileResponse
*/
protected function file($file, $fileName = null, $disposition = ResponseHeaderBag::DISPOSITION_ATTACHMENT)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doesnt this new method manifest as a BC break when the subclassing controller already contains a file method?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.