file_save_upload

file_save_upload($form_field_name, $validators = array(), $destination = FALSE, $delta = NULL, $replace = FILE_EXISTS_RENAME)

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.

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 callback functions used to validate the file. See file_validate() for a full discussion of the array format. If the array is empty, it will be set up to call file_validate_extensions() 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 ['file_validate_extensions' => '']. (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.

int $replace: (optional) The replace behavior when the destination file already exists. Possible values include:

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.

File

core/modules/file/file.module, line 714
Defines a "managed_file" Form API field and a "file" field for Field module.

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
function file_save_upload($form_field_name, $validators = array(), $destination = FALSE, $delta = NULL, $replace = FILE_EXISTS_RENAME) {
  $user = \Drupal::currentUser();
  static $upload_cache;
 
  $all_files = \Drupal::request()->files->get('files', array());
  // 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 = array($file_upload);
  }
 
  $files = array();
  foreach ($uploaded_files as $i => $file_info) {
    // Check for file upload errors and return FALSE for this file if a lower
    // level system error occurred. For a complete list of errors:
    switch ($file_info->getError()) {
      case UPLOAD_ERR_INI_SIZE:
      case UPLOAD_ERR_FORM_SIZE:
        drupal_set_message(t('The file %file could not be saved because it exceeds %maxsize, the maximum allowed size for uploads.', array('%file' => $file_info->getFilename(), '%maxsize' => format_size(file_upload_max_size()))), 'error');
        $files[$i] = FALSE;
        continue;
 
      case UPLOAD_ERR_PARTIAL:
      case UPLOAD_ERR_NO_FILE:
        drupal_set_message(t('The file %file could not be saved because the upload did not complete.', array('%file' => $file_info->getFilename())), 'error');
        $files[$i] = FALSE;
        continue;
 
      case UPLOAD_ERR_OK:
        // Final check that this is a valid upload, if it isn't, use the
        // default error handler.
        if (is_uploaded_file($file_info->getRealPath())) {
          break;
        }
 
        // Unknown error
      default:
        drupal_set_message(t('The file %file could not be saved. An unknown error has occurred.', array('%file' => $file_info->getFilename())), 'error');
        $files[$i] = FALSE;
        continue;
 
    }
    // Begin building file entity.
    $values = array(
      'uid' => $user->id(),
      'status' => 0,
      'filename' => $file_info->getClientOriginalName(),
      'uri' => $file_info->getRealPath(),
      'filesize' => $file_info->getSize(),
    );
    $values['filemime'] = \Drupal::service('file.mime_type.guesser')->guess($values['filename']);
    $file = File::create($values);
 
    $extensions = '';
    if (isset($validators['file_validate_extensions'])) {
      if (isset($validators['file_validate_extensions'][0])) {
        // Build the list of non-munged extensions if the caller provided them.
        $extensions = $validators['file_validate_extensions'][0];
      }
      else {
        // If 'file_validate_extensions' is set and the list is empty then the
        // caller wants to allow any extension. In this case we have to remove the
        // validator or else it will reject all extensions.
        unset($validators['file_validate_extensions']);
      }
    }
    else {
      // No validator was provided, so add one using the default list.
      // Build a default non-munged safe list for file_munge_filename().
      $extensions = 'jpg jpeg gif png txt doc xls pdf ppt pps odt ods odp';
      $validators['file_validate_extensions'] = array();
      $validators['file_validate_extensions'][0] = $extensions;
    }
 
    if (!empty($extensions)) {
      // Munge the filename to protect against possible malicious extension
      // hiding within an unknown file type (ie: filename.html.foo).
      $file->setFilename(file_munge_filename($file->getFilename(), $extensions));
    }
 
    // Rename potentially executable files, to help prevent exploits (i.e. will
    // rename filename.php.foo and filename.php to filename.php.foo.txt and
    // filename.php.txt, respectively). Don't rename if 'allow_insecure_uploads'
    // evaluates to TRUE.
    if (!\Drupal::config('system.file')->get('allow_insecure_uploads') && preg_match('/\.(php|pl|py|cgi|asp|js)(\.|$)/i', $file->getFilename()) && (substr($file->getFilename(), -4) != '.txt')) {
      $file->setMimeType('text/plain');
      // The destination filename will also later be used to create the URI.
      $file->setFilename($file->getFilename() . '.txt');
      // The .txt extension may not be in the allowed list of extensions. We have
      // to add it here or else the file upload will fail.
      if (!empty($extensions)) {
        $validators['file_validate_extensions'][0] .= ' txt';
        drupal_set_message(t('For security reasons, your upload has been renamed to %filename.', array('%filename' => $file->getFilename())));
      }
    }
 
    // If the destination is not provided, use the temporary directory.
    if (empty($destination)) {
      $destination = 'temporary://';
    }
 
    // Assert that the destination contains a valid stream.
    $destination_scheme = file_uri_scheme($destination);
    if (!file_stream_wrapper_valid_scheme($destination_scheme)) {
      drupal_set_message(t('The file could not be uploaded because the destination %destination is invalid.', array('%destination' => $destination)), 'error');
      $files[$i] = FALSE;
      continue;
    }
 
    $file->source = $form_field_name;
    // A file URI may already have a trailing slash or look like "public://".
    if (substr($destination, -1) != '/') {
      $destination .= '/';
    }
    $file->destination = file_destination($destination . $file->getFilename(), $replace);
    // If file_destination() returns FALSE then $replace === FILE_EXISTS_ERROR and
    // there's an existing file so we need to bail.
    if ($file->destination === FALSE) {
      drupal_set_message(t('The file %source could not be uploaded because a file by that name already exists in the destination %directory.', array('%source' => $form_field_name, '%directory' => $destination)), 'error');
      $files[$i] = FALSE;
      continue;
    }
 
    // Add in our check of the file name length.
    $validators['file_validate_name_length'] = array();
 
    // Call the validation functions specified by this function's caller.
    $errors = file_validate($file, $validators);
 
    // Check for errors.
    if (!empty($errors)) {
      $message = array(
        'error' => array(
          '#markup' => t('The specified file %name could not be uploaded.', array('%name' => $file->getFilename())),
        ),
        'item_list' => array(
          '#theme' => 'item_list',
          '#items' => $errors,
        ),
      );
      // @todo Add support for render arrays in drupal_set_message()? See
      drupal_set_message(\Drupal::service('renderer')->renderPlain($message), 'error');
      $files[$i] = FALSE;
      continue;
    }
 
    // Move uploaded files from PHP's upload_tmp_dir to Drupal's temporary
    // directory. This overcomes open_basedir restrictions for future file
    // operations.
    $file->setFileUri($file->destination);
    if (!drupal_move_uploaded_file($file_info->getRealPath(), $file->getFileUri())) {
      drupal_set_message(t('File upload error. Could not move uploaded file.'), 'error');
      \Drupal::logger('file')->notice('Upload error. Could not move uploaded file %file to destination %destination.', array('%file' => $file->getFilename(), '%destination' => $file->getFileUri()));
      $files[$i] = FALSE;
      continue;
    }
 
    // Set the permissions on the new file.
    drupal_chmod($file->getFileUri());
 
    // If we are replacing an existing file re-use its database record.
    // @todo Do not create a new entity in order to update it. See
    if ($replace == FILE_EXISTS_REPLACE) {
      $existing_files = entity_load_multiple_by_properties('file', array('uri' => $file->getFileUri()));
      if (count($existing_files)) {
        $existing = reset($existing_files);
        $file->fid = $existing->id();
        $file->setOriginalId($existing->id());
      }
    }
 
    // If we made it this far it's safe to record this file in the database.
    $file->save();
    $files[$i] = $file;
  }
 
  // Add files to the cache.
  $upload_cache[$form_field_name] = $files;
 
  return isset($delta) ? $files[$delta] : $files;
}
doc_Drupal
2025-01-10 15:47:30
Comments
Leave a Comment

Please login to continue.