diff --git a/core/modules/file/file.module b/core/modules/file/file.module index f17b773d8c..33307c1e3f 100644 --- a/core/modules/file/file.module +++ b/core/modules/file/file.module @@ -27,6 +27,7 @@ use Drupal\file\Entity\File; use Drupal\file\FileInterface; use Drupal\file\Upload\FileValidationException; +use Drupal\file\Upload\FormUploadedFile; use Symfony\Component\HttpFoundation\File\Exception\FileException as SymfonyFileException; use Symfony\Component\HttpFoundation\File\Exception\FormSizeFileException; use Symfony\Component\HttpFoundation\File\Exception\IniSizeFileException; @@ -913,7 +914,8 @@ function file_save_upload($form_field_name, $validators = [], $destination = FAL /** @var \Symfony\Component\HttpFoundation\File\UploadedFile $uploaded_file */ foreach ($uploaded_files as $i => $uploaded_file) { try { - $result = $file_upload_handler->handleFileUpload($uploaded_file, $validators, $destination, $replace); + $form_uploaded_file = new FormUploadedFile($uploaded_file); + $result = $file_upload_handler->handleFileUpload($form_uploaded_file, $validators, $destination, $replace); $file = $result->getFile(); // If the filename has been modified, let the user know. if ($result->isRenamed()) { diff --git a/core/modules/file/file.services.yml b/core/modules/file/file.services.yml index 9e7d1c3ff8..080603e5be 100644 --- a/core/modules/file/file.services.yml +++ b/core/modules/file/file.services.yml @@ -15,13 +15,13 @@ services: file.field_upload_handler: class: Drupal\file\Upload\FileFieldUploadHandler - arguments: [ '@file.upload_handler', '@token' ] - file.stream_uploader: - class: Drupal\file\Upload\FileStreamUploader + arguments: [ '@file_system', '@entity_type.manager', '@stream_wrapper_manager', '@event_dispatcher', '@file.mime_type.guesser', '@current_user', '@request_stack', '@token' ] + file.raw_file_uploader: + class: Drupal\file\Upload\RawFileUploader arguments: [ '@file_system' ] file.upload_filename_extractor: class: Drupal\file\Upload\FilenameExtractor arguments: [ '@file_system' ] file.upload_access_checker: class: Drupal\file\Upload\FileFieldUploadAccessChecker - arguments: ['@entity_type.manager', '@current_user'] + arguments: [ '@entity_type.manager', '@current_user' ] diff --git a/core/modules/file/src/Upload/FileFieldUploadHandler.php b/core/modules/file/src/Upload/FileFieldUploadHandler.php index b93f2407c5..cced6f2c83 100644 --- a/core/modules/file/src/Upload/FileFieldUploadHandler.php +++ b/core/modules/file/src/Upload/FileFieldUploadHandler.php @@ -2,6 +2,11 @@ namespace Drupal\file\Upload; +use Drupal\Core\Entity\EntityTypeManagerInterface; +use Drupal\Core\File\Exception\DirectoryNotReadyException; +use Drupal\Core\File\FileSystemInterface; +use Drupal\Core\Session\AccountInterface; +use Drupal\Core\StreamWrapper\StreamWrapperManagerInterface; use Drupal\Core\Utility\Token; use Drupal\Component\Render\PlainTextOutput; use Drupal\Component\Utility\Bytes; @@ -9,12 +14,14 @@ use Drupal\Core\Field\FieldDefinitionInterface; use Drupal\Core\Render\BubbleableMetadata; use Drupal\file\Plugin\Field\FieldType\FileFieldItemList; -use Symfony\Component\HttpFoundation\File\UploadedFile; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\Mime\MimeTypeGuesserInterface; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; /** * Provides an upload handler for file fields. */ -class FileFieldUploadHandler { +class FileFieldUploadHandler extends FileUploadHandler { /** * The file upload handler. @@ -31,18 +38,34 @@ class FileFieldUploadHandler { protected $token; /** - * @param \Drupal\file\Upload\FileUploadHandler $fileUploadHandler + * Constructs a FileUploadHandler object. + * + * @param \Drupal\Core\File\FileSystemInterface $fileSystem + * The file system service. + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager + * The entity type manager. + * @param \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface $streamWrapperManager + * The stream wrapper manager. + * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $eventDispatcher + * The event dispatcher. + * @param \Symfony\Component\Mime\MimeTypeGuesserInterface $mimeTypeGuesser + * The MIME type guesser. + * @param \Drupal\Core\Session\AccountInterface $currentUser + * The current user. + * @param \Symfony\Component\HttpFoundation\RequestStack $requestStack + * The request stack. * @param \Drupal\Core\Utility\Token $token + * The token service. */ - public function __construct(FileUploadHandler $fileUploadHandler, Token $token) { - $this->fileUploadHandler = $fileUploadHandler; + public function __construct(FileSystemInterface $fileSystem, EntityTypeManagerInterface $entityTypeManager, StreamWrapperManagerInterface $streamWrapperManager, EventDispatcherInterface $eventDispatcher, MimeTypeGuesserInterface $mimeTypeGuesser, AccountInterface $currentUser, RequestStack $requestStack, Token $token) { + parent::__construct($fileSystem, $entityTypeManager, $streamWrapperManager, $eventDispatcher, $mimeTypeGuesser, $currentUser, $requestStack); $this->token = $token; } /** * Handles file field uploads. * - * @param \Symfony\Component\HttpFoundation\File\UploadedFile $uploadedFile + * @param \Drupal\file\Upload\UploadedFileInterface $uploadedFile * The uploaded file. * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition * The field definition. @@ -59,7 +82,7 @@ public function __construct(FileUploadHandler $fileUploadHandler, Token $token) * @throws \Drupal\file\Upload\FileValidationException * Thrown when file validation fails. */ - public function handleFileUploadForField(UploadedFile $uploadedFile, FieldDefinitionInterface $field_definition): FileUploadResult { + public function handleFileUploadForField(UploadedFileInterface $uploadedFile, FieldDefinitionInterface $field_definition): FileUploadResult { assert(is_a($field_definition->getClass(), FileFieldItemList::class, TRUE)); $settings = $field_definition->getSettings(); @@ -67,7 +90,19 @@ public function handleFileUploadForField(UploadedFile $uploadedFile, FieldDefini $validators = $this->getUploadValidators($field_definition); $destination = $this->getUploadLocation($settings); - return $this->fileUploadHandler->handleFileUpload($uploadedFile, $validators, $destination); + // Check the destination file path is writable. + if (!$this->fileSystem->prepareDirectory($destination, FileSystemInterface::CREATE_DIRECTORY)) { + throw new DirectoryNotReadyException('Destination file path %s is not writable'); + } + + return $this->handleFileUpload($uploadedFile, $validators, $destination); + } + + /** + * {@inheritdoc} + */ + protected function moveUploadedFile($uploadedFile, $uri) { + return $this->fileSystem->move($uploadedFile->getPathname(), $uri); } /** diff --git a/core/modules/file/src/Upload/FileUploadHandler.php b/core/modules/file/src/Upload/FileUploadHandler.php index bcfc983002..0b0e7041b6 100644 --- a/core/modules/file/src/Upload/FileUploadHandler.php +++ b/core/modules/file/src/Upload/FileUploadHandler.php @@ -21,7 +21,6 @@ use Symfony\Component\HttpFoundation\File\Exception\NoFileException; use Symfony\Component\HttpFoundation\File\Exception\NoTmpDirFileException; use Symfony\Component\HttpFoundation\File\Exception\PartialFileException; -use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\Mime\MimeTypeGuesserInterface; @@ -115,7 +114,7 @@ public function __construct(FileSystemInterface $fileSystem, EntityTypeManagerIn /** * Creates a file from an upload. * - * @param \Symfony\Component\HttpFoundation\File\UploadedFile $uploadedFile + * @param \Drupal\file\Upload\UploadedFileInterface $uploadedFile * The uploaded file object. * @param array $validators * The validators to run against the uploaded file. @@ -131,16 +130,9 @@ public function __construct(FileSystemInterface $fileSystem, EntityTypeManagerIn * @return \Drupal\file\Upload\FileUploadResult * The created file entity. * - * @throws \Symfony\Component\HttpFoundation\File\Exception\FileException - * Thrown when a file upload error occurred. - * @throws \Drupal\Core\File\Exception\FileWriteException - * Thrown when there is an error moving the file. - * @throws \Drupal\Core\File\Exception\FileException - * Thrown when a file system error occurs. - * @throws \Drupal\file\Upload\FileValidationException - * Thrown when file validation fails. + * @throws \Drupal\Core\Entity\EntityStorageException */ - public function handleFileUpload(UploadedFile $uploadedFile, array $validators = [], string $destination = 'temporary://', int $replace = FileSystemInterface::EXISTS_REPLACE): FileUploadResult { + public function handleFileUpload(UploadedFileInterface $uploadedFile, array $validators = [], string $destination = 'temporary://', int $replace = FileSystemInterface::EXISTS_REPLACE): FileUploadResult { $originalName = $uploadedFile->getClientOriginalName(); if (!$uploadedFile->isValid()) { @@ -217,7 +209,8 @@ public function handleFileUpload(UploadedFile $uploadedFile, array $validators = } $file->setFileUri($destinationFilename); - if (!$this->fileSystem->moveUploadedFile($uploadedFile->getRealPath(), $file->getFileUri())) { + + if (!$this->moveUploadedFile($uploadedFile, $file->getFileUri())) { throw new FileWriteException('File upload error. Could not move uploaded file.'); } @@ -270,6 +263,21 @@ public function handleFileUpload(UploadedFile $uploadedFile, array $validators = return $result; } + /** + * Move the uploaded file from the temporary path to the destination. + * + * @param \Drupal\file\Upload\UploadedFileInterface $uploadedFile + * The uploaded file. + * @param string $uri + * The destination URI. + * + * @return bool + * Returns FALSE if moving failed. + */ + protected function moveUploadedFile(UploadedFileInterface $uploadedFile, string $uri) { + return $this->fileSystem->moveUploadedFile($uploadedFile->getRealPath(), $uri); + } + /** * Gets the list of allowed extensions and updates the validators. * diff --git a/core/modules/file/src/Upload/FormUploadedFile.php b/core/modules/file/src/Upload/FormUploadedFile.php new file mode 100644 index 0000000000..24eb8a3f3e --- /dev/null +++ b/core/modules/file/src/Upload/FormUploadedFile.php @@ -0,0 +1,85 @@ +uploadedFile = $uploadedFile; + } + + /** + * {@inheritdoc} + */ + public function getClientOriginalName(): string { + return $this->uploadedFile->getClientOriginalName(); + } + + /** + * {@inheritdoc} + */ + public function isValid(): bool { + return $this->uploadedFile->isValid(); + } + + /** + * {@inheritdoc} + */ + public function getErrorMessage(): string { + return $this->uploadedFile->getErrorMessage(); + } + + /** + * {@inheritdoc} + */ + public function getError(): int { + return $this->uploadedFile->getError(); + } + + /** + * {@inheritdoc} + */ + public function getSize(): int { + return $this->uploadedFile->getSize(); + } + + /** + * {@inheritdoc} + */ + public function getRealPath() { + return $this->uploadedFile->getRealPath(); + } + + /** + * {@inheritdoc} + */ + public function getPathname() { + return $this->uploadedFile->getPathname(); + } + + /** + * {@inheritdoc} + */ + public function getFilename() { + return $this->uploadedFile->getFilename(); + } + +} diff --git a/core/modules/file/src/Upload/FileStreamUploader.php b/core/modules/file/src/Upload/RawFileUploader.php similarity index 72% rename from core/modules/file/src/Upload/FileStreamUploader.php rename to core/modules/file/src/Upload/RawFileUploader.php index cb148db3d1..cc6bfb15c7 100644 --- a/core/modules/file/src/Upload/FileStreamUploader.php +++ b/core/modules/file/src/Upload/RawFileUploader.php @@ -3,12 +3,11 @@ namespace Drupal\file\Upload; use Drupal\Core\File\FileSystemInterface; -use Symfony\Component\HttpFoundation\File\UploadedFile; /** - * Saves uploaded file data. + * Uploads raw file data. */ -class FileStreamUploader { +class RawFileUploader { /** * The file system. @@ -42,16 +41,16 @@ public function __construct(FileSystemInterface $fileSystem) { * (Optional) The type of the file as provided by PHP; null defaults to * application/octet-stream * - * @return \Symfony\Component\HttpFoundation\File\UploadedFile - * The uploaded file. + * @return \Drupal\file\Upload\RawUploadedFile + * The raw uploaded file. */ - public function streamUploadData(string $filename, string $source = 'php://input', string $mimeType = NULL): UploadedFile { + public function uploadRawFile(string $filename, string $source = 'php://input', string $mimeType = 'application/octet-stream'): RawUploadedFile { // 'rb' is needed so reading works correctly on Windows environments too. $file_data = fopen($source, 'rb'); $temp_file_path = $this->fileSystem->tempnam('temporary://', 'file'); if ($temp_file_path === FALSE) { - return new UploadedFile('temporary://', $filename, $mimeType, \UPLOAD_ERR_NO_TMP_DIR); + return new RawUploadedFile('temporary://', $filename, $mimeType, \UPLOAD_ERR_NO_TMP_DIR); } $temp_file = fopen($temp_file_path, 'wb'); @@ -60,7 +59,7 @@ public function streamUploadData(string $filename, string $source = 'php://input // Close the input file stream since we can't proceed with the upload. // Don't try to close $temp_file since it's FALSE at this point. fclose($file_data); - return new UploadedFile($temp_file_path, $filename, $mimeType, \UPLOAD_ERR_NO_FILE); + return new RawUploadedFile($temp_file_path, $filename, $mimeType, \UPLOAD_ERR_NO_FILE); } while (!feof($file_data)) { @@ -70,14 +69,14 @@ public function streamUploadData(string $filename, string $source = 'php://input // Close the file streams. fclose($temp_file); fclose($file_data); - return new UploadedFile($temp_file, $filename, $mimeType, \UPLOAD_ERR_NO_FILE); + return new RawUploadedFile($temp_file, $filename, $mimeType, \UPLOAD_ERR_NO_FILE); } if (fwrite($temp_file, $read) === FALSE) { // Close the file streams. fclose($temp_file); fclose($file_data); - return new UploadedFile($temp_file_path, $filename, $mimeType, \UPLOAD_ERR_CANT_WRITE); + return new RawUploadedFile($temp_file_path, $filename, $mimeType, \UPLOAD_ERR_CANT_WRITE); } } @@ -87,7 +86,7 @@ public function streamUploadData(string $filename, string $source = 'php://input // Close the input stream. fclose($file_data); - return new UploadedFile($temp_file_path, $filename); + return new RawUploadedFile($temp_file_path, $filename); } } diff --git a/core/modules/file/src/Upload/RawUploadedFile.php b/core/modules/file/src/Upload/RawUploadedFile.php new file mode 100644 index 0000000000..ac992c6544 --- /dev/null +++ b/core/modules/file/src/Upload/RawUploadedFile.php @@ -0,0 +1,121 @@ +error); + $this->originalName = $originalName; + $this->mimeType = $mimeType; + $this->error = $error; + } + + /** + * {@inheritdoc} + */ + public function getClientOriginalName(): string { + return $this->originalName; + } + + /** + * {@inheritdoc} + */ + public function isValid(): bool { + return \UPLOAD_ERR_OK === $this->error; + } + + /** + * {@inheritdoc} + * + * @see \Symfony\Component\HttpFoundation\File\UploadedFile::getErrorMessage() + */ + public function getErrorMessage(): string { + static $errors = [ + \UPLOAD_ERR_INI_SIZE => 'The file "%s" exceeds your upload_max_filesize ini directive (limit is %d KiB).', + \UPLOAD_ERR_FORM_SIZE => 'The file "%s" exceeds the upload limit defined in your form.', + \UPLOAD_ERR_PARTIAL => 'The file "%s" was only partially uploaded.', + \UPLOAD_ERR_NO_FILE => 'No file was uploaded.', + \UPLOAD_ERR_CANT_WRITE => 'The file "%s" could not be written on disk.', + \UPLOAD_ERR_NO_TMP_DIR => 'File could not be uploaded: missing temporary directory.', + \UPLOAD_ERR_EXTENSION => 'File upload was stopped by a PHP extension.', + ]; + + $errorCode = $this->error; + $maxFilesize = \UPLOAD_ERR_INI_SIZE === $errorCode ? UploadedFile::getMaxFilesize() / 1024 : 0; + $message = $errors[$errorCode] ?? 'The file "%s" was not uploaded due to an unknown error.'; + + return sprintf($message, $this->getClientOriginalName(), $maxFilesize); + } + + /** + * {@inheritdoc} + */ + public function getError(): int { + return $this->error; + } + + /** + * {@inheritdoc} + */ + public function getSize(): int { + return parent::getSize(); + } + + /** + * {@inheritdoc} + */ + public function getRealPath() { + return parent::getRealPath(); + } + + /** + * {@inheritdoc} + */ + public function getPathname() { + return parent::getPathname(); + } + + /** + * {@inheritdoc} + */ + public function getFilename() { + return parent::getFilename(); + } + +} diff --git a/core/modules/file/src/Upload/UploadedFileInterface.php b/core/modules/file/src/Upload/UploadedFileInterface.php new file mode 100644 index 0000000000..b544a8a8ec --- /dev/null +++ b/core/modules/file/src/Upload/UploadedFileInterface.php @@ -0,0 +1,85 @@ +FALSE if the file does not exist. + * + * @see https://php.net/manual/en/splfileinfo.getrealpath.php + */ + public function getRealPath(); + + /** + * Gets the path to the file. + * + * @return string + * The path to the file. + * + * @see https://php.net/manual/en/splfileinfo.getpathname.php + */ + public function getPathname(); + + /** + * Gets the filename. + * + * @return string + * The filename. + * + * @see https://php.net/manual/en/splfileinfo.getfilename.php + */ + public function getFilename(); + +} diff --git a/core/modules/file/tests/src/Unit/Upload/FileFieldUploadHandlerTest.php b/core/modules/file/tests/src/Unit/Upload/FileFieldUploadHandlerTest.php index b6181b0a0e..c2ef042242 100644 --- a/core/modules/file/tests/src/Unit/Upload/FileFieldUploadHandlerTest.php +++ b/core/modules/file/tests/src/Unit/Upload/FileFieldUploadHandlerTest.php @@ -8,6 +8,7 @@ use Drupal\file\Upload\FileFieldUploadHandler; use Drupal\file\Upload\FileUploadHandler; use Drupal\file\Upload\FileUploadResult; +use Drupal\file\Upload\FormUploadedFile; use Drupal\Tests\UnitTestCase; use org\bovigo\vfs\vfsStream; use Prophecy\Argument; @@ -32,6 +33,7 @@ public function testHandleFileUploadForField() { $path = vfsStream::url("tmp/$filename"); $uploadedFile = new UploadedFile($path, $filename, NULL, NULL, TRUE); + $formUploadedFile = new FormUploadedFile($uploadedFile); $fieldDefinition = $this->prophesize(FieldDefinitionInterface::class); $fieldDefinition->getClass() @@ -50,7 +52,7 @@ public function testHandleFileUploadForField() { $token = $this->prophesize(Token::class); $handler = new FileFieldUploadHandler($fileUploadHandler->reveal(), $token->reveal()); - $result = $handler->handleFileUploadForField($uploadedFile, $fieldDefinition->reveal()); + $result = $handler->handleFileUploadForField($formUploadedFile, $fieldDefinition->reveal()); $this->assertNotNull($result); } diff --git a/core/modules/file/tests/src/Unit/Upload/FileStreamUploaderTest.php b/core/modules/file/tests/src/Unit/Upload/RawFileUploaderTest.php similarity index 73% rename from core/modules/file/tests/src/Unit/Upload/FileStreamUploaderTest.php rename to core/modules/file/tests/src/Unit/Upload/RawFileUploaderTest.php index 7da65a15e5..57d8f031e6 100644 --- a/core/modules/file/tests/src/Unit/Upload/FileStreamUploaderTest.php +++ b/core/modules/file/tests/src/Unit/Upload/RawFileUploaderTest.php @@ -3,7 +3,7 @@ namespace Drupal\Tests\file\Unit\Upload; use Drupal\Core\File\FileSystemInterface; -use Drupal\file\Upload\FileStreamUploader; +use Drupal\file\Upload\RawFileUploader; use Drupal\Tests\UnitTestCase; use org\bovigo\vfs\vfsStream; use Prophecy\Argument; @@ -11,10 +11,10 @@ /** * Tests the file stream uploader. * - * @coversDefaultClass \Drupal\file\Upload\FileStreamUploader + * @coversDefaultClass \Drupal\file\Upload\RawFileUploader * @group file */ -class FileStreamUploaderTest extends UnitTestCase { +class RawFileUploaderTest extends UnitTestCase { public function testUpload() { @@ -25,12 +25,12 @@ public function testUpload() { $fileSystem->tempnam(Argument::any(), Argument::any()) ->willReturn($tempFile->url()); - $uploader = new FileStreamUploader($fileSystem->reveal()); + $uploader = new RawFileUploader($fileSystem->reveal()); $filename = "foo.txt"; $input = vfsStream::newFile($filename)->at($vfsRoot)->setContent("foo"); - $uploadedFile = $uploader->streamUploadData($filename, $input->url()); + $uploadedFile = $uploader->uploadRawFile($filename, $input->url()); $this->assertNotNull($uploadedFile); $this->assertEquals($filename, $uploadedFile->getClientOriginalName()); diff --git a/core/modules/jsonapi/jsonapi.services.yml b/core/modules/jsonapi/jsonapi.services.yml index bb89b246c3..e08720c657 100644 --- a/core/modules/jsonapi/jsonapi.services.yml +++ b/core/modules/jsonapi/jsonapi.services.yml @@ -199,7 +199,7 @@ services: - '@file.field_definition_resolver' - '@file.upload_access_checker' - '@file.upload_filename_extractor' - - '@file.stream_uploader' + - '@file.raw_file_uploader' - '@logger.channel.file' # Event subscribers. diff --git a/core/modules/jsonapi/src/Controller/FileUpload.php b/core/modules/jsonapi/src/Controller/FileUpload.php index 359768c968..2454a4daef 100644 --- a/core/modules/jsonapi/src/Controller/FileUpload.php +++ b/core/modules/jsonapi/src/Controller/FileUpload.php @@ -2,6 +2,7 @@ namespace Drupal\jsonapi\Controller; +use Drupal\Component\Render\PlainTextOutput; use Drupal\Core\Access\AccessResultReasonInterface; use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Entity\EntityFieldManagerInterface; @@ -16,7 +17,7 @@ use Drupal\file\Upload\FileFieldUploadAccessChecker; use Drupal\file\Upload\FileFieldUploadHandler; use Drupal\file\Upload\FilenameExtractor; -use Drupal\file\Upload\FileStreamUploader; +use Drupal\file\Upload\RawFileUploader; use Drupal\file\Upload\FileValidationException; use Drupal\jsonapi\Entity\EntityValidationTrait; use Drupal\jsonapi\JsonApiResource\JsonApiDocumentTopLevel; @@ -106,7 +107,7 @@ class FileUpload { /** * The file stream uploader. * - * @var \Drupal\file\Upload\FileStreamUploader + * @var \Drupal\file\Upload\RawFileUploader */ protected $fileStreamUploader; @@ -136,12 +137,12 @@ class FileUpload { * The file upload access checker. * @param \Drupal\file\Upload\FilenameExtractor $filename_extractor * The filename extractor. - * @param \Drupal\file\Upload\FileStreamUploader $file_stream_uploader + * @param \Drupal\file\Upload\RawFileUploader $file_stream_uploader * The file stream uploader. * @param \Psr\Log\LoggerInterface $logger * The logger. */ - public function __construct(AccountInterface $current_user, EntityFieldManagerInterface $field_manager, TemporaryJsonapiFileFieldUploader $file_uploader, HttpKernelInterface $http_kernel, FileFieldUploadHandler $upload_handler, FileFieldDefinitionResolver $field_resolver, FileFieldUploadAccessChecker $access_checker, FilenameExtractor $filename_extractor, FileStreamUploader $file_stream_uploader, LoggerInterface $logger) { + public function __construct(AccountInterface $current_user, EntityFieldManagerInterface $field_manager, TemporaryJsonapiFileFieldUploader $file_uploader, HttpKernelInterface $http_kernel, FileFieldUploadHandler $upload_handler, FileFieldDefinitionResolver $field_resolver, FileFieldUploadAccessChecker $access_checker, FilenameExtractor $filename_extractor, RawFileUploader $file_stream_uploader, LoggerInterface $logger) { $this->currentUser = $current_user; $this->fieldManager = $field_manager; $this->fileUploader = $file_uploader; @@ -252,7 +253,7 @@ protected function doHandleFileUpload(Request $request, ResourceType $resource_t $this->ensureFileUploadAccess($field_definition); $filename = $this->filenameExtractor->extractFilename($request); - $uploadedFile = $this->fileStreamUploader->streamUploadData($filename); + $uploadedFile = $this->fileStreamUploader->uploadRawFile($filename); try { $result = $this->fileFieldUploadHandler->handleFileUploadForField($uploadedFile, $field_definition); @@ -263,7 +264,10 @@ protected function doHandleFileUpload(Request $request, ResourceType $resource_t } catch (FileValidationException $e) { $message = "Unprocessable Entity: file validation failed.\n"; - $message .= implode("\n", $e->getErrors()); + $message .= implode("\n", array_map(function (string $error) { + return PlainTextOutput::renderFromHtml($error); + }, $e->getErrors())); + throw new UnprocessableEntityHttpException($message); }