class FileUpload
Handles file upload requests.
@internal JSON:API maintains no PHP API. The API is the HTTP API. This class may change at any time and could break any dependencies on it.
Hierarchy
- class \Drupal\jsonapi\Controller\FileUpload uses \Drupal\jsonapi\Entity\EntityValidationTrait
Expanded class hierarchy of FileUpload
See also
https://www.drupal.org/project/drupal/issues/3032787
2 string references to 'FileUpload'
- jsonapi.services.yml in core/modules/ jsonapi/ jsonapi.services.yml 
- core/modules/jsonapi/jsonapi.services.yml
- ManagedFile::processManagedFile in core/modules/ file/ src/ Element/ ManagedFile.php 
- Render API callback: Expands the managed_file element type.
1 service uses FileUpload
- jsonapi.file_upload in core/modules/ jsonapi/ jsonapi.services.yml 
- Drupal\jsonapi\Controller\FileUpload
File
- 
              core/modules/ jsonapi/ src/ Controller/ FileUpload.php, line 40 
Namespace
Drupal\jsonapi\ControllerView source
class FileUpload {
  use EntityValidationTrait;
  
  /**
   * The current user making the request.
   *
   * @var \Drupal\Core\Session\AccountInterface
   */
  protected $currentUser;
  
  /**
   * The field manager.
   *
   * @var \Drupal\Core\Entity\EntityFieldManagerInterface
   */
  protected $fieldManager;
  
  /**
   * The file uploader.
   *
   * @var \Drupal\jsonapi\Controller\TemporaryJsonapiFileFieldUploader
   */
  protected $fileUploader;
  
  /**
   * An HTTP kernel for making subrequests.
   *
   * @var \Symfony\Component\HttpKernel\HttpKernelInterface
   */
  protected $httpKernel;
  
  /**
   * Creates a new FileUpload instance.
   *
   * @param \Drupal\Core\Session\AccountInterface $current_user
   *   The current user.
   * @param \Drupal\Core\Entity\EntityFieldManagerInterface $field_manager
   *   The entity field manager.
   * @param \Drupal\jsonapi\Controller\TemporaryJsonapiFileFieldUploader $file_uploader
   *   The file uploader.
   * @param \Symfony\Component\HttpKernel\HttpKernelInterface $http_kernel
   *   An HTTP kernel for making subrequests.
   */
  public function __construct(AccountInterface $current_user, EntityFieldManagerInterface $field_manager, TemporaryJsonapiFileFieldUploader $file_uploader, HttpKernelInterface $http_kernel) {
    $this->currentUser = $current_user;
    $this->fieldManager = $field_manager;
    $this->fileUploader = $file_uploader;
    $this->httpKernel = $http_kernel;
  }
  
  /**
   * Handles JSON:API file upload requests.
   *
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The HTTP request object.
   * @param \Drupal\jsonapi\ResourceType\ResourceType $resource_type
   *   The JSON:API resource type for the current request.
   * @param string $file_field_name
   *   The file field for which the file is to be uploaded.
   * @param \Drupal\Core\Entity\FieldableEntityInterface $entity
   *   The entity for which the file is to be uploaded.
   *
   * @return \Drupal\jsonapi\ResourceResponse
   *   The response object.
   *
   * @throws \Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException
   *   Thrown when there are validation errors.
   * @throws \Drupal\Core\Entity\EntityStorageException
   *   Thrown if the upload's target resource could not be saved.
   * @throws \Exception
   *   Thrown if an exception occurs during a subrequest to fetch the newly
   *   created file entity.
   */
  public function handleFileUploadForExistingResource(Request $request, ResourceType $resource_type, $file_field_name, FieldableEntityInterface $entity) {
    $file_field_name = $resource_type->getInternalName($file_field_name);
    $field_definition = $this->validateAndLoadFieldDefinition($resource_type->getEntityTypeId(), $resource_type->getBundle(), $file_field_name);
    static::ensureFileUploadAccess($this->currentUser, $field_definition, $entity);
    $filename = $this->fileUploader
      ->validateAndParseContentDispositionHeader($request);
    $file = $this->fileUploader
      ->handleFileUploadForField($field_definition, $filename, $this->currentUser);
    if ($file instanceof EntityConstraintViolationListInterface) {
      $violations = $file;
      $message = "Unprocessable Entity: file validation failed.\n";
      $message .= implode("\n", array_map(function (ConstraintViolationInterface $violation) {
        return PlainTextOutput::renderFromHtml($violation->getMessage());
      }, (array) $violations->getIterator()));
      throw new UnprocessableEntityHttpException($message);
    }
    if ($resource_type->getFieldByInternalName($file_field_name)
      ->hasOne()) {
      $entity->{$file_field_name} = $file;
    }
    else {
      $entity->get($file_field_name)
        ->appendItem($file);
    }
    static::validate($entity, [
      $file_field_name,
    ]);
    $entity->save();
    $route_parameters = [
      'entity' => $entity->uuid(),
    ];
    $route_name = sprintf('jsonapi.%s.%s.related', $resource_type->getTypeName(), $resource_type->getPublicName($file_field_name));
    $related_url = Url::fromRoute($route_name, $route_parameters)->toString(TRUE);
    $request = Request::create($related_url->getGeneratedUrl(), 'GET', [], $request->cookies
      ->all(), [], $request->server
      ->all());
    return $this->httpKernel
      ->handle($request, HttpKernelInterface::SUB_REQUEST);
  }
  
  /**
   * Handles JSON:API file upload requests.
   *
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The HTTP request object.
   * @param \Drupal\jsonapi\ResourceType\ResourceType $resource_type
   *   The JSON:API resource type for the current request.
   * @param string $file_field_name
   *   The file field for which the file is to be uploaded.
   *
   * @return \Drupal\jsonapi\ResourceResponse
   *   The response object.
   *
   * @throws \Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException
   *   Thrown when there are validation errors.
   */
  public function handleFileUploadForNewResource(Request $request, ResourceType $resource_type, $file_field_name) {
    $file_field_name = $resource_type->getInternalName($file_field_name);
    $field_definition = $this->validateAndLoadFieldDefinition($resource_type->getEntityTypeId(), $resource_type->getBundle(), $file_field_name);
    static::ensureFileUploadAccess($this->currentUser, $field_definition);
    $filename = $this->fileUploader
      ->validateAndParseContentDispositionHeader($request);
    $file = $this->fileUploader
      ->handleFileUploadForField($field_definition, $filename, $this->currentUser);
    if ($file instanceof EntityConstraintViolationListInterface) {
      $violations = $file;
      $message = "Unprocessable Entity: file validation failed.\n";
      $message .= implode("\n", array_map(function (ConstraintViolationInterface $violation) {
        return PlainTextOutput::renderFromHtml($violation->getMessage());
      }, iterator_to_array($violations)));
      throw new UnprocessableEntityHttpException($message);
    }
    // @todo Remove line below in favor of commented line in https://www.drupal.org/project/drupal/issues/2878463.
    $self_link = new Link(new CacheableMetadata(), Url::fromRoute('jsonapi.file--file.individual', [
      'entity' => $file->uuid(),
    ]), 'self');
    /* $self_link = new Link(new CacheableMetadata(), $this->entity->toUrl('jsonapi'), ['self']); */
    $links = new LinkCollection([
      'self' => $self_link,
    ]);
    $relatable_resource_types = $resource_type->getRelatableResourceTypesByField($resource_type->getPublicName($file_field_name));
    $file_resource_type = reset($relatable_resource_types);
    $resource_object = ResourceObject::createFromEntity($file_resource_type, $file);
    return new ResourceResponse(new JsonApiDocumentTopLevel(new ResourceObjectData([
      $resource_object,
    ], 1), new NullIncludedData(), $links), 201, []);
  }
  
  /**
   * Ensures that the given account is allowed to upload a file.
   *
   * @param \Drupal\Core\Session\AccountInterface $account
   *   The account for which access should be checked.
   * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
   *   The field for which the file is to be uploaded.
   * @param \Drupal\Core\Entity\FieldableEntityInterface|null $entity
   *   The entity, if one exists, for which the file is to be uploaded.
   */
  protected static function ensureFileUploadAccess(AccountInterface $account, FieldDefinitionInterface $field_definition, FieldableEntityInterface $entity = NULL) {
    $access_result = $entity ? TemporaryJsonapiFileFieldUploader::checkFileUploadAccess($account, $field_definition, $entity) : TemporaryJsonapiFileFieldUploader::checkFileUploadAccess($account, $field_definition);
    if (!$access_result->isAllowed()) {
      $reason = 'The current user is not permitted to upload a file for this field.';
      if ($access_result instanceof AccessResultReasonInterface) {
        $reason .= ' ' . $access_result->getReason();
      }
      throw new AccessDeniedHttpException($reason);
    }
  }
  
  /**
   * Validates and loads a field definition instance.
   *
   * @param string $entity_type_id
   *   The entity type ID the field is attached to.
   * @param string $bundle
   *   The bundle the field is attached to.
   * @param string $field_name
   *   The field name.
   *
   * @return \Drupal\Core\Field\FieldDefinitionInterface
   *   The field definition.
   *
   * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
   *   Thrown when the field does not exist.
   * @throws \Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException
   *   Thrown when the target type of the field is not a file, or the current
   *   user does not have 'edit' access for the field.
   */
  protected function validateAndLoadFieldDefinition($entity_type_id, $bundle, $field_name) {
    $field_definitions = $this->fieldManager
      ->getFieldDefinitions($entity_type_id, $bundle);
    if (!isset($field_definitions[$field_name])) {
      throw new NotFoundHttpException(sprintf('Field "%s" does not exist.', $field_name));
    }
    /** @var \Drupal\Core\Field\FieldDefinitionInterface $field_definition */
    $field_definition = $field_definitions[$field_name];
    if ($field_definition->getSetting('target_type') !== 'file') {
      throw new AccessDeniedException(sprintf('"%s" is not a file field', $field_name));
    }
    return $field_definition;
  }
}Members
| Title Sort descending | Modifiers | Object type | Summary | 
|---|---|---|---|
| EntityValidationTrait::validate | protected static | function | Verifies that an entity does not violate any validation constraints. | 
| FileUpload::$currentUser | protected | property | The current user making the request. | 
| FileUpload::$fieldManager | protected | property | The field manager. | 
| FileUpload::$fileUploader | protected | property | The file uploader. | 
| FileUpload::$httpKernel | protected | property | An HTTP kernel for making subrequests. | 
| FileUpload::ensureFileUploadAccess | protected static | function | Ensures that the given account is allowed to upload a file. | 
| FileUpload::handleFileUploadForExistingResource | public | function | Handles JSON:API file upload requests. | 
| FileUpload::handleFileUploadForNewResource | public | function | Handles JSON:API file upload requests. | 
| FileUpload::validateAndLoadFieldDefinition | protected | function | Validates and loads a field definition instance. | 
| FileUpload::__construct | public | function | Creates a new FileUpload instance. | 
Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.
