You are here

public function FileUploadHandler::handleFileUpload in Drupal 10

Creates a file from an upload.

Parameters

\Drupal\file\Upload\UploadedFileInterface $uploadedFile: The uploaded file object.

array $validators: The validators to run against the uploaded file.

string $destination: The destination directory.

int $replace: Replace behavior when the destination file already exists:

Return value

\Drupal\file\Upload\FileUploadResult The created file entity.

Throws

\Symfony\Component\HttpFoundation\File\Exception\FileException Thrown when a file upload error occurred.

\Drupal\Core\File\Exception\FileWriteException Thrown when there is an error moving the file.

\Drupal\Core\File\Exception\FileException Thrown when a file system error occurs.

\Drupal\file\Upload\FileValidationException Thrown when file validation fails.

File

core/modules/file/src/Upload/FileUploadHandler.php, line 142

Class

FileUploadHandler
Handles validating and creating file entities from file uploads.

Namespace

Drupal\file\Upload

Code

public function handleFileUpload(UploadedFileInterface $uploadedFile, array $validators = [], string $destination = 'temporary://', int $replace = FileSystemInterface::EXISTS_REPLACE) : FileUploadResult {
  $originalName = $uploadedFile
    ->getClientOriginalName();
  if (!$uploadedFile
    ->isValid()) {
    switch ($uploadedFile
      ->getError()) {
      case \UPLOAD_ERR_INI_SIZE:
        throw new IniSizeFileException($uploadedFile
          ->getErrorMessage());
      case \UPLOAD_ERR_FORM_SIZE:
        throw new FormSizeFileException($uploadedFile
          ->getErrorMessage());
      case \UPLOAD_ERR_PARTIAL:
        throw new PartialFileException($uploadedFile
          ->getErrorMessage());
      case \UPLOAD_ERR_NO_FILE:
        throw new NoFileException($uploadedFile
          ->getErrorMessage());
      case \UPLOAD_ERR_CANT_WRITE:
        throw new CannotWriteFileException($uploadedFile
          ->getErrorMessage());
      case \UPLOAD_ERR_NO_TMP_DIR:
        throw new NoTmpDirFileException($uploadedFile
          ->getErrorMessage());
      case \UPLOAD_ERR_EXTENSION:
        throw new ExtensionFileException($uploadedFile
          ->getErrorMessage());
    }
    throw new FileException($uploadedFile
      ->getErrorMessage());
  }
  $extensions = $this
    ->handleExtensionValidation($validators);

  // Assert that the destination contains a valid stream.
  $destinationScheme = $this->streamWrapperManager::getScheme($destination);
  if (!$this->streamWrapperManager
    ->isValidScheme($destinationScheme)) {
    throw new InvalidStreamWrapperException(sprintf('The file could not be uploaded because the destination "%s" is invalid.', $destination));
  }

  // A file URI may already have a trailing slash or look like "public://".
  if (substr($destination, -1) != '/') {
    $destination .= '/';
  }

  // Call an event to sanitize the filename and to attempt to address security
  // issues caused by common server setups.
  $event = new FileUploadSanitizeNameEvent($originalName, $extensions);
  $this->eventDispatcher
    ->dispatch($event);
  $filename = $event
    ->getFilename();
  $mimeType = $this->mimeTypeGuesser
    ->guessMimeType($filename);
  $destinationFilename = $this->fileSystem
    ->getDestinationFilename($destination . $filename, $replace);
  if ($destinationFilename === FALSE) {
    throw new FileExistsException(sprintf('Destination file "%s" exists', $destinationFilename));
  }
  $file = File::create([
    'uid' => $this->currentUser
      ->id(),
    'status' => 0,
    'uri' => $uploadedFile
      ->getRealPath(),
  ]);

  // This will be replaced later with a filename based on the destination.
  $file
    ->setFilename($filename);
  $file
    ->setMimeType($mimeType);
  $file
    ->setSize($uploadedFile
    ->getSize());

  // Add in our check of the file name length.
  $validators['file_validate_name_length'] = [];

  // Call the validation functions specified by this function's caller.
  $errors = file_validate($file, $validators);
  if (!empty($errors)) {
    throw new FileValidationException('File validation failed', $filename, $errors);
  }
  $file
    ->setFileUri($destinationFilename);
  if (!$this
    ->moveUploadedFile($uploadedFile, $file
    ->getFileUri())) {
    throw new FileWriteException('File upload error. Could not move uploaded file.');
  }

  // Update the filename with any changes as a result of security or renaming
  // due to an existing file.
  $file
    ->setFilename($this->fileSystem
    ->basename($file
    ->getFileUri()));
  if ($replace === FileSystemInterface::EXISTS_REPLACE) {
    $existingFile = $this
      ->loadByUri($file
      ->getFileUri());
    if ($existingFile) {
      $file->fid = $existingFile
        ->id();
      $file
        ->setOriginalId($existingFile
        ->id());
    }
  }
  $result = (new FileUploadResult())
    ->setOriginalFilename($originalName)
    ->setSanitizedFilename($filename)
    ->setFile($file);

  // If the filename has been modified, let the user know.
  if ($event
    ->isSecurityRename()) {
    $result
      ->setSecurityRename();
  }

  // Set the permissions on the new file.
  $this->fileSystem
    ->chmod($file
    ->getFileUri());

  // We can now validate the file object itself before it's saved.
  $violations = $file
    ->validate();
  foreach ($violations as $violation) {
    $errors[] = $violation
      ->getMessage();
  }
  if (!empty($errors)) {
    throw new FileValidationException('File validation failed', $filename, $errors);
  }

  // If we made it this far it's safe to record this file in the database.
  $file
    ->save();

  // Allow an anonymous user who creates a non-public file to see it. See
  // \Drupal\file\FileAccessControlHandler::checkAccess().
  if ($this->currentUser
    ->isAnonymous() && $destinationScheme !== 'public') {
    $session = $this->requestStack
      ->getCurrentRequest()
      ->getSession();
    $allowed_temp_files = $session
      ->get('anonymous_allowed_file_ids', []);
    $allowed_temp_files[$file
      ->id()] = $file
      ->id();
    $session
      ->set('anonymous_allowed_file_ids', $allowed_temp_files);
  }
  return $result;
}