function file_save_upload

Same name in other branches
  1. 7.x includes/file.inc \file_save_upload()
  2. 9 core/modules/file/file.module \file_save_upload()
  3. 8.9.x core/modules/file/file.module \file_save_upload()
  4. 10 core/modules/file/file.module \file_save_upload()

Saves file uploads to a new location.

The files will be added to the {file_managed} table as temporary files. Temporary files are periodically cleaned. Use the 'file.usage' service to register the usage of the file which will automatically mark it as permanent.

Note that this function does not support correct form error handling. The file upload widgets in core do support this. It is advised to use these in any custom form, instead of calling this function.

Parameters

string $form_field_name: A string that is the associative array key of the upload form element in the form array.

array $validators: (optional) An associative array of Validation Constraint plugins used to validate the file. If the array is empty, 'FileExtension' will be used by default with a safe list of extensions, as follows: "jpg jpeg gif png txt doc xls pdf ppt pps odt ods odp". To allow all extensions, you must explicitly set this array to ['FileExtension' => []]. (Beware: this is not safe and should only be allowed for trusted users, if at all.)

string|false $destination: (optional) A string containing the URI that the file should be copied to. This must be a stream wrapper URI. If this value is omitted or set to FALSE, Drupal's temporary files scheme will be used ("temporary://").

null|int $delta: (optional) The delta of the file to return the file entity. Defaults to NULL.

\Drupal\Core\File\FileExists|int $fileExists: (optional) The replace behavior when the destination file already exists.

Return value

array|\Drupal\file\FileInterface|null|false An array of file entities or a single file entity if $delta != NULL. Each array element contains the file entity if the upload succeeded or FALSE if there was an error. Function returns NULL if no file was uploaded.

Throws

\ValueError Thrown if $fileExists is a legacy int and not a valid value.

See also

_file_save_upload_from_form()

4 calls to file_save_upload()
FileSaveUploadTest::testFileSaveUploadEmptyExtensions in core/modules/file/tests/src/Kernel/FileSaveUploadTest.php
Tests file_save_upload() with empty extensions.
FileTestForm::submitForm in core/modules/file/tests/file_test/src/Form/FileTestForm.php
Form submission handler.
UpdateManagerInstall::submitForm in core/modules/update/src/Form/UpdateManagerInstall.php
Form submission handler.
_file_save_upload_from_form in core/modules/file/file.module
Saves form file uploads.

File

core/modules/file/file.module, line 334

Code

function file_save_upload($form_field_name, $validators = [], $destination = FALSE, $delta = NULL, FileExists|int $fileExists = FileExists::Rename) {
    if (!$fileExists instanceof FileExists) {
        // @phpstan-ignore staticMethod.deprecated
        $fileExists = FileExists::fromLegacyInt($fileExists, __METHOD__);
    }
    static $upload_cache;
    $all_files = \Drupal::request()->files
        ->get('files', []);
    // Make sure there's an upload to process.
    if (empty($all_files[$form_field_name])) {
        return NULL;
    }
    $file_upload = $all_files[$form_field_name];
    // Return cached objects without processing since the file will have
    // already been processed and the paths in $_FILES will be invalid.
    if (isset($upload_cache[$form_field_name])) {
        if (isset($delta)) {
            return $upload_cache[$form_field_name][$delta];
        }
        return $upload_cache[$form_field_name];
    }
    // Prepare uploaded files info. Representation is slightly different
    // for multiple uploads and we fix that here.
    $uploaded_files = $file_upload;
    if (!is_array($file_upload)) {
        $uploaded_files = [
            $file_upload,
        ];
    }
    if ($destination === FALSE || $destination === NULL) {
        $destination = 'temporary://';
    }
    
    /** @var \Drupal\file\Upload\FileUploadHandler $file_upload_handler */
    $file_upload_handler = \Drupal::service('file.upload_handler');
    
    /** @var \Drupal\Core\Render\RendererInterface $renderer */
    $renderer = \Drupal::service('renderer');
    $files = [];
    
    /** @var \Symfony\Component\HttpFoundation\File\UploadedFile $uploaded_file */
    foreach ($uploaded_files as $i => $uploaded_file) {
        try {
            $form_uploaded_file = new FormUploadedFile($uploaded_file);
            $result = $file_upload_handler->handleFileUpload($form_uploaded_file, $validators, $destination, $fileExists);
            if ($result->hasViolations()) {
                $errors = [];
                foreach ($result->getViolations() as $violation) {
                    $errors[] = $violation->getMessage();
                }
                $message = [
                    'error' => [
                        '#markup' => t('The specified file %name could not be uploaded.', [
                            '%name' => $uploaded_file->getClientOriginalName(),
                        ]),
                    ],
                    'item_list' => [
                        '#theme' => 'item_list',
                        '#items' => $errors,
                    ],
                ];
                // @todo Add support for render arrays in
                // \Drupal\Core\Messenger\MessengerInterface::addMessage()?
                // @see https://www.drupal.org/node/2505497.
                \Drupal::messenger()->addError($renderer->renderInIsolation($message));
                $files[$i] = FALSE;
                continue;
            }
            $file = $result->getFile();
            // If the filename has been modified, let the user know.
            if ($result->isRenamed()) {
                if ($result->isSecurityRename()) {
                    $message = t('For security reasons, your upload has been renamed to %filename.', [
                        '%filename' => $file->getFilename(),
                    ]);
                }
                else {
                    $message = t('Your upload has been renamed to %filename.', [
                        '%filename' => $file->getFilename(),
                    ]);
                }
                \Drupal::messenger()->addStatus($message);
            }
            $files[$i] = $file;
        } catch (FileExistsException) {
            \Drupal::messenger()->addError(t('Destination file "%file" exists', [
                '%file' => $destination . $uploaded_file->getFilename(),
            ]));
            $files[$i] = FALSE;
        } catch (InvalidStreamWrapperException) {
            \Drupal::messenger()->addError(t('The file could not be uploaded because the destination "%destination" is invalid.', [
                '%destination' => $destination,
            ]));
            $files[$i] = FALSE;
        } catch (FileWriteException) {
            \Drupal::messenger()->addError(t('File upload error. Could not move uploaded file.'));
            \Drupal::logger('file')->notice('Upload error. Could not move uploaded file %file to destination %destination.', [
                '%file' => $uploaded_file->getClientOriginalName(),
                '%destination' => $destination . '/' . $uploaded_file->getClientOriginalName(),
            ]);
            $files[$i] = FALSE;
        } catch (FileException) {
            \Drupal::messenger()->addError(t('The file %filename could not be uploaded because the name is invalid.', [
                '%filename' => $uploaded_file->getClientOriginalName(),
            ]));
            $files[$i] = FALSE;
        } catch (LockAcquiringException) {
            \Drupal::messenger()->addError(t('File already locked for writing.'));
            $files[$i] = FALSE;
        }
    }
    // Add files to the cache.
    $upload_cache[$form_field_name] = $files;
    return isset($delta) ? $files[$delta] : $files;
}

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.