You are here

public function FileUpload::saveFileUpload in GraphQL 8.4

Validates an uploaded file, saves it and returns a file upload response.

Based on several file upload handlers, see _file_save_upload_single() \Drupal\file\Plugin\Field\FieldType\FileItem \Drupal\file\Plugin\rest\resource\FileUploadResource.

Parameters

\Symfony\Component\HttpFoundation\File\UploadedFile $uploaded_file: The file entity to upload.

array $settings: File settings as specified in regular file field config. Contains keys:

  • file_directory: Where to upload the file
  • uri_scheme: Uri scheme to upload the file to (eg public://, private://)
  • file_extensions: List of valid file extensions (eg [xml, pdf])
  • max_filesize: Maximum allowed size of uploaded file.

Return value

\Drupal\graphql\GraphQL\Response\FileUploadResponse The file upload response containing file entity or list of violations.

Throws

\Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException

\Drupal\Component\Plugin\Exception\PluginNotFoundException

\RuntimeException

1 call to FileUpload::saveFileUpload()
FileUpload::saveMultipleFileUploads in src/GraphQL/Utility/FileUpload.php
Validates uploaded files, saves them and returns a file upload response.

File

src/GraphQL/Utility/FileUpload.php, line 166

Class

FileUpload
Service to manage file uploads within GraphQL mutations.

Namespace

Drupal\graphql\GraphQL\Utility

Code

public function saveFileUpload(UploadedFile $uploaded_file, array $settings) : FileUploadResponse {
  $response = new FileUploadResponse();

  // Check for file upload errors and return FALSE for this file if a lower
  // level system error occurred.
  // @see http://php.net/manual/features.file-upload.errors.php.
  switch ($uploaded_file
    ->getError()) {
    case UPLOAD_ERR_INI_SIZE:
    case UPLOAD_ERR_FORM_SIZE:
      $maxUploadSize = format_size($this
        ->getMaxUploadSize($settings));
      $response
        ->addViolation($this
        ->t('The file @file could not be saved because it exceeds @maxsize, the maximum allowed size for uploads.', [
        '@file' => $uploaded_file
          ->getClientOriginalName(),
        '@maxsize' => $maxUploadSize,
      ]));
      return $response;
    case UPLOAD_ERR_PARTIAL:
    case UPLOAD_ERR_NO_FILE:
      $response
        ->addViolation($this
        ->t('The file "@file" could not be saved because the upload did not complete.', [
        '@file' => $uploaded_file
          ->getClientOriginalName(),
      ]));
      return $response;
    case UPLOAD_ERR_OK:

      // Final check that this is a valid upload, if it isn't, use the
      // default error handler.
      if ($uploaded_file
        ->isValid()) {
        break;
      }
    default:
      $response
        ->addViolation($this
        ->t('Unknown error while uploading the file "@file".', [
        '@file' => $uploaded_file
          ->getClientOriginalName(),
      ]));
      $this->logger
        ->error('Error while uploading the file "@file" with an error code "@code".', [
        '@file' => $uploaded_file
          ->getFilename(),
        '@code' => $uploaded_file
          ->getError(),
      ]);
      return $response;
  }
  if (empty($settings['uri_scheme']) || empty($settings['file_directory'])) {
    throw new \RuntimeException('uri_scheme or file_directory missing in settings');
  }
  $destination = $this
    ->getUploadLocation($settings);

  // Check the destination file path is writable.
  if (!$this->fileSystem
    ->prepareDirectory($destination, FileSystemInterface::CREATE_DIRECTORY)) {
    $response
      ->addViolation($this
      ->t('Unknown error while uploading the file "@file".', [
      '@file' => $uploaded_file
        ->getClientOriginalName(),
    ]));
    $this->logger
      ->error('Could not create directory "@upload_directory".', [
      "@upload_directory" => $destination,
    ]);
    return $response;
  }
  $validators = $this
    ->getUploadValidators($settings);
  $prepared_filename = $this
    ->prepareFilename($uploaded_file
    ->getClientOriginalName(), $validators);

  // Create the file.
  $file_uri = "{$destination}/{$prepared_filename}";
  $temp_file_path = $uploaded_file
    ->getRealPath();
  $file_uri = $this->fileSystem
    ->getDestinationFilename($file_uri, FileSystemInterface::EXISTS_RENAME);

  // Lock based on the prepared file URI.
  $lock_id = $this
    ->generateLockIdFromFileUri($file_uri);
  if (!$this->lock
    ->acquire($lock_id)) {
    $response
      ->addViolation($this
      ->t('Unknown error while uploading the file "@file".', [
      '@file' => $uploaded_file
        ->getClientOriginalName(),
    ]));
    return $response;
  }
  try {

    // Begin building file entity.

    /** @var \Drupal\file\FileInterface $file */
    $file = $this->fileStorage
      ->create([]);
    $file
      ->setOwnerId($this->currentUser
      ->id());
    $file
      ->setFilename($prepared_filename);
    $file
      ->setMimeType($this->mimeTypeGuesser
      ->guess($prepared_filename));
    $file
      ->setFileUri($temp_file_path);

    // Set the size. This is done in File::preSave() but we validate the file
    // before it is saved.
    $file
      ->setSize(@filesize($temp_file_path));

    // Validate against file_validate() first with the temporary path.
    $errors = file_validate($file, $validators);
    if (!empty($errors)) {
      $response
        ->addViolations($errors);
      return $response;
    }
    $file
      ->setFileUri($file_uri);

    // Move the file to the correct location after validation. Use
    // FileSystemInterface::EXISTS_ERROR as the file location has already been
    // determined above in FileSystem::getDestinationFilename().
    try {
      $this->fileSystem
        ->move($temp_file_path, $file_uri, FileSystemInterface::EXISTS_ERROR);
    } catch (FileException $e) {
      $response
        ->addViolation($this
        ->t('Unknown error while uploading the file "@file".', [
        '@file' => $uploaded_file
          ->getClientOriginalName(),
      ]));
      $this->logger
        ->error('Unable to move file from "@file" to "@destination".', [
        '@file' => $uploaded_file
          ->getRealPath(),
        '@destination' => $file
          ->getFileUri(),
      ]);
      return $response;
    }

    // Validate the file entity against entity-level validation now after the
    // file has moved.
    if (!$this
      ->validate($file, $validators, $response)) {
      return $response;
    }
    $file
      ->save();
    $response
      ->setFileEntity($file);
    return $response;
  } finally {

    // This will always be executed before any return statement or exception
    // in the try {} block.
    $this->lock
      ->release($lock_id);
  }
}