FileDownloadController.php 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. <?php
  2. namespace Drupal\system;
  3. use Drupal\Core\Controller\ControllerBase;
  4. use Drupal\Core\StreamWrapper\StreamWrapperManagerInterface;
  5. use Symfony\Component\DependencyInjection\ContainerInterface;
  6. use Symfony\Component\HttpFoundation\BinaryFileResponse;
  7. use Symfony\Component\HttpFoundation\Request;
  8. use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
  9. use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
  10. /**
  11. * System file controller.
  12. */
  13. class FileDownloadController extends ControllerBase {
  14. /**
  15. * The stream wrapper manager.
  16. *
  17. * @var \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface
  18. */
  19. protected $streamWrapperManager;
  20. /**
  21. * FileDownloadController constructor.
  22. *
  23. * @param \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface $streamWrapperManager
  24. * The stream wrapper manager.
  25. */
  26. public function __construct(StreamWrapperManagerInterface $streamWrapperManager = NULL) {
  27. if (!$streamWrapperManager) {
  28. @trigger_error('Calling FileDownloadController::__construct() without the $streamWrapperManager argument is deprecated in drupal:8.8.0. The $streamWrapperManager argument will be required in drupal:9.0.0. See https://www.drupal.org/node/3035273', E_USER_DEPRECATED);
  29. $streamWrapperManager = \Drupal::service('stream_wrapper_manager');
  30. }
  31. $this->streamWrapperManager = $streamWrapperManager;
  32. }
  33. /**
  34. * {@inheritdoc}
  35. */
  36. public static function create(ContainerInterface $container) {
  37. return new static(
  38. $container->get('stream_wrapper_manager')
  39. );
  40. }
  41. /**
  42. * Handles private file transfers.
  43. *
  44. * Call modules that implement hook_file_download() to find out if a file is
  45. * accessible and what headers it should be transferred with. If one or more
  46. * modules returned headers the download will start with the returned headers.
  47. * If a module returns -1 an AccessDeniedHttpException will be thrown. If the
  48. * file exists but no modules responded an AccessDeniedHttpException will be
  49. * thrown. If the file does not exist a NotFoundHttpException will be thrown.
  50. *
  51. * @see hook_file_download()
  52. *
  53. * @param \Symfony\Component\HttpFoundation\Request $request
  54. * The request object.
  55. * @param string $scheme
  56. * The file scheme, defaults to 'private'.
  57. *
  58. * @return \Symfony\Component\HttpFoundation\BinaryFileResponse
  59. * The transferred file as response.
  60. *
  61. * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
  62. * Thrown when the requested file does not exist.
  63. * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
  64. * Thrown when the user does not have access to the file.
  65. */
  66. public function download(Request $request, $scheme = 'private') {
  67. $target = $request->query->get('file');
  68. // Merge remaining path arguments into relative file path.
  69. $uri = $scheme . '://' . $target;
  70. if ($this->streamWrapperManager->isValidScheme($scheme) && is_file($uri)) {
  71. // Let other modules provide headers and controls access to the file.
  72. $headers = $this->moduleHandler()->invokeAll('file_download', [$uri]);
  73. foreach ($headers as $result) {
  74. if ($result == -1) {
  75. throw new AccessDeniedHttpException();
  76. }
  77. }
  78. if (count($headers)) {
  79. // \Drupal\Core\EventSubscriber\FinishResponseSubscriber::onRespond()
  80. // sets response as not cacheable if the Cache-Control header is not
  81. // already modified. We pass in FALSE for non-private schemes for the
  82. // $public parameter to make sure we don't change the headers.
  83. return new BinaryFileResponse($uri, 200, $headers, $scheme !== 'private');
  84. }
  85. throw new AccessDeniedHttpException();
  86. }
  87. throw new NotFoundHttpException();
  88. }
  89. }