diff --git a/core/modules/field/tests/src/Kernel/Migrate/d6/MigrateFieldInstanceTest.php b/core/modules/field/tests/src/Kernel/Migrate/d6/MigrateFieldInstanceTest.php
index 6c4e29c4be..2b0d8ed793 100644
--- a/core/modules/field/tests/src/Kernel/Migrate/d6/MigrateFieldInstanceTest.php
+++ b/core/modules/field/tests/src/Kernel/Migrate/d6/MigrateFieldInstanceTest.php
@@ -84,6 +84,7 @@ public function testFieldInstanceMigration() {
       'display_field' => FALSE,
       'display_default' => FALSE,
       'uri_scheme' => 'public',
+      'uri_scheme_field' => FALSE,
       'handler' => 'default:file',
       'handler_settings' => [],
     ];
diff --git a/core/modules/file/config/schema/file.schema.yml b/core/modules/file/config/schema/file.schema.yml
index 336550904b..620ba81e3e 100644
--- a/core/modules/file/config/schema/file.schema.yml
+++ b/core/modules/file/config/schema/file.schema.yml
@@ -38,6 +38,9 @@ field.storage_settings.file:
     uri_scheme:
       type: string
       label: 'Upload destination'
+    uri_scheme_field:
+      type: boolean
+      label: 'Enable "file system used for upload" field'
 
 field.value.file:
   type: mapping
diff --git a/core/modules/file/file.field.inc b/core/modules/file/file.field.inc
index a3b8868cd0..1de01923b8 100644
--- a/core/modules/file/file.field.inc
+++ b/core/modules/file/file.field.inc
@@ -28,6 +28,12 @@ function template_preprocess_file_widget_multiple(&$variables) {
   // Build up a table of applicable fields.
   $headers = [];
   $headers[] = t('File information');
+  if ($element['#uri_scheme_field']) {
+    $headers[] = [
+      'data' => t('Location'),
+      'class' => ['radio'],
+    ];
+  }
   if ($element['#display_field']) {
     $headers[] = [
       'data' => t('Display'),
@@ -64,8 +70,11 @@ function template_preprocess_file_widget_multiple(&$variables) {
       }
     }
 
-    // Delay rendering of the "Display" option and the weight selector, so that
-    // each can be rendered later in its own column.
+    // Delay rendering of the "Display" and "Location" option and the weight
+    // selector, so that each can be rendered later in its own column.
+    if ($element['#uri_scheme_field']) {
+      hide($widget['uri_scheme']);
+    }
     if ($element['#display_field']) {
       hide($widget['display']);
     }
@@ -74,6 +83,14 @@ function template_preprocess_file_widget_multiple(&$variables) {
     // Render everything else together in a column, without the normal wrappers.
     $widget['#theme_wrappers'] = [];
     $information = \Drupal::service('renderer')->render($widget);
+    $location = '';
+    if ($element['#uri_scheme_field']) {
+      unset($widget['uri_scheme']['#title']);
+      $location = [
+        'data' => render($widget['uri_scheme']),
+        'class' => ['radio'],
+      ];
+    }
     $display = '';
     if ($element['#display_field']) {
       unset($widget['display']['#title']);
@@ -88,6 +105,9 @@ function template_preprocess_file_widget_multiple(&$variables) {
     // Arrange the row with all of the rendered columns.
     $row = [];
     $row[] = $information;
+    if ($element['#uri_scheme_field']) {
+      $row[] = $location;
+    }
     if ($element['#display_field']) {
       $row[] = $display;
     }
diff --git a/core/modules/file/src/Element/ManagedFile.php b/core/modules/file/src/Element/ManagedFile.php
index ca4e887a1b..c2eec30d6d 100644
--- a/core/modules/file/src/Element/ManagedFile.php
+++ b/core/modules/file/src/Element/ManagedFile.php
@@ -69,6 +69,11 @@ public static function valueCallback(&$element, $input, FormStateInterface $form
       $input['fids'] = $fids;
       $return = $input;
 
+      // Override uri scheme.
+      if (isset($input['uri_scheme'])) {
+        $element['#upload_location'] = str_replace($element['#uri_scheme_default'] . '://', $input['uri_scheme'] . '://', $element['#upload_location']);
+      }
+
       // Uploads take priority over all other values.
       if ($files = file_managed_file_save_upload($element, $form_state)) {
         if ($element['#multiple']) {
@@ -89,6 +94,20 @@ public static function valueCallback(&$element, $input, FormStateInterface $form
           }
         }
 
+        // Move files to another uri scheme if a change is detected.
+        // Depends on $fids having only 1 entry.
+        if (!empty($fid) && isset($input['uri_scheme']) && count($fids) === 1) {
+          $file = File::load($fid);
+          $current_uri_scheme = explode('://', $file->getFileUri())[0];
+          if ($input['uri_scheme'] !== $current_uri_scheme && file_exists($file->getFileUri())) {
+            $destination = str_replace($current_uri_scheme . '://', $input['uri_scheme'] . '://', $file->getFileUri());
+            $directory = \Drupal::service('file_system')->dirname($destination);
+            if (file_prepare_directory($directory, FILE_CREATE_DIRECTORY)) {
+              file_move($file, $destination);
+            }
+          }
+        }
+
         // Load files if the FIDs have changed to confirm they exist.
         if (!empty($input['fids'])) {
           $fids = [];
diff --git a/core/modules/file/src/Plugin/Field/FieldType/FileItem.php b/core/modules/file/src/Plugin/Field/FieldType/FileItem.php
index 542be122df..325f642111 100644
--- a/core/modules/file/src/Plugin/Field/FieldType/FileItem.php
+++ b/core/modules/file/src/Plugin/Field/FieldType/FileItem.php
@@ -37,6 +37,7 @@ public static function defaultStorageSettings() {
       'display_field' => FALSE,
       'display_default' => FALSE,
       'uri_scheme' => file_default_scheme(),
+      'uri_scheme_field' => FALSE,
     ] + parent::defaultStorageSettings();
   }
 
@@ -139,6 +140,13 @@ public function storageSettingsForm(array &$form, FormStateInterface $form_state
       '#disabled' => $has_data,
     ];
 
+    $element['uri_scheme_field'] = [
+      '#type' => 'checkbox',
+      '#title' => $this->t('Enable <em>file system used for upload</em> field'),
+      '#default_value' => $this->getSetting('uri_scheme_field'),
+      '#description' => $this->t('The "file system used for upload" option allows users to choose where the final file should be stored: public or private separately for each file.'),
+    ];
+
     return $element;
   }
 
diff --git a/core/modules/file/src/Plugin/Field/FieldWidget/FileWidget.php b/core/modules/file/src/Plugin/Field/FieldWidget/FileWidget.php
index 587a396fc6..df65ab0d09 100644
--- a/core/modules/file/src/Plugin/Field/FieldWidget/FileWidget.php
+++ b/core/modules/file/src/Plugin/Field/FieldWidget/FileWidget.php
@@ -3,6 +3,7 @@
 namespace Drupal\file\Plugin\Field\FieldWidget;
 
 use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FieldItemListInterface;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
@@ -11,6 +12,7 @@
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
 use Drupal\Core\Render\Element;
 use Drupal\Core\Render\ElementInfoManagerInterface;
+use Drupal\Core\StreamWrapper\StreamWrapperInterface;
 use Drupal\file\Element\ManagedFile;
 use Drupal\file\Entity\File;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -185,6 +187,7 @@ protected function formMultipleElements(FieldItemListInterface $items, array &$f
       // act on field types without these expected settings.
       $field_settings = $this->getFieldSettings() + ['display_field' => NULL];
       $elements['#display_field'] = (bool) $field_settings['display_field'];
+      $elements['#uri_scheme_field'] = (bool) $field_settings['uri_scheme_field'];
 
       // Add some properties that will eventually be added to the file upload
       // field. These are added here so that they may be referenced easily
@@ -214,6 +217,7 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
       'display_default' => NULL,
       'display_field' => NULL,
       'description_field' => NULL,
+      'uri_scheme_field' => NULL,
     ];
 
     $cardinality = $this->fieldDefinition->getFieldStorageDefinition()->getCardinality();
@@ -240,6 +244,8 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
       '#entity_type' => $items->getEntity()->getEntityTypeId(),
       '#display_field' => (bool) $field_settings['display_field'],
       '#display_default' => $field_settings['display_default'],
+      '#uri_scheme_field' => $field_settings['uri_scheme_field'],
+      '#uri_scheme_default' => $field_settings['uri_scheme'],
       '#description_field' => $field_settings['description_field'],
       '#cardinality' => $cardinality,
     ];
@@ -404,6 +410,23 @@ public static function process($element, FormStateInterface $form_state, $form)
       ];
     }
 
+    // Add the uri scheme field if this is the add new file widget.
+    if ($element['#uri_scheme_field']) {
+      $scheme_options = \Drupal::service('stream_wrapper_manager')->getNames(StreamWrapperInterface::WRITE_VISIBLE);
+      /** @var \Drupal\file\Entity\File $file */
+      if (isset($item['target_id'])) {
+        $file = \Drupal::service('entity_type.manager')->getStorage('file')->load($item['target_id']);
+        $current_uri_scheme = explode('://', $file->getFileUri())[0];
+      }
+      $element['uri_scheme'] = [
+        '#type' => 'radios',
+        '#title' => t('Upload destination'),
+        '#options' => $scheme_options,
+        '#default_value' => isset($current_uri_scheme) ? $current_uri_scheme : $element['#uri_scheme_default'],
+        '#required' => TRUE,
+      ];
+    }
+
     // Add the description field if enabled.
     if ($element['#description_field'] && $item['fids']) {
       $config = \Drupal::config('file.settings');
diff --git a/core/modules/file/tests/src/Functional/FileFieldTestBase.php b/core/modules/file/tests/src/Functional/FileFieldTestBase.php
index d6096874d6..44e9237dbd 100644
--- a/core/modules/file/tests/src/Functional/FileFieldTestBase.php
+++ b/core/modules/file/tests/src/Functional/FileFieldTestBase.php
@@ -224,7 +224,7 @@ public function uploadNodeFiles(array $files, $field_name, $nid_or_type, $new_re
         $edit[$name][] = $file_path;
       }
     }
-    $this->drupalPostForm("node/$nid/edit", $edit, t('Save and keep published'));
+    $this->drupalPostForm("node/$nid/edit", $edit, t('Save'));
 
     return $nid;
   }
@@ -240,7 +240,7 @@ public function removeNodeFile($nid, $new_revision = TRUE) {
     ];
 
     $this->drupalPostForm('node/' . $nid . '/edit', [], t('Remove'));
-    $this->drupalPostForm(NULL, $edit, t('Save and keep published'));
+    $this->drupalPostForm(NULL, $edit, t('Save'));
   }
 
   /**
@@ -253,7 +253,7 @@ public function replaceNodeFile($file, $field_name, $nid, $new_revision = TRUE)
     ];
 
     $this->drupalPostForm('node/' . $nid . '/edit', [], t('Remove'));
-    $this->drupalPostForm(NULL, $edit, t('Save and keep published'));
+    $this->drupalPostForm(NULL, $edit, t('Save'));
   }
 
   /**
diff --git a/core/modules/file/tests/src/Functional/FileFieldUploadDestinationTest.php b/core/modules/file/tests/src/Functional/FileFieldUploadDestinationTest.php
new file mode 100644
index 0000000000..980518915a
--- /dev/null
+++ b/core/modules/file/tests/src/Functional/FileFieldUploadDestinationTest.php
@@ -0,0 +1,133 @@
+<?php
+
+namespace Drupal\Tests\file\Functional;
+
+use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\Core\File\FileSystem;
+use Drupal\field\Entity\FieldStorageConfig;
+use Drupal\file\Entity\File;
+
+/**
+ * Tests the upload destination setting of file fields.
+ *
+ * @group file
+ */
+class FileFieldUploadDestinationTest extends FileFieldTestBase {
+
+  /**
+   * Tests setting the upload destination field on a file field.
+   */
+  public function testUploadDestinationToggle() {
+    // Create a file field with uri_scheme_field.
+    $field_name = strtolower($this->randomMachineName());
+    $type_name = 'article';
+    $field_storage_settings = [
+      'uri_scheme_field' => '1',
+      'uri_scheme' => 'private',
+      'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
+    ];
+    $this->createFileField($field_name, 'node', $type_name, $field_storage_settings);
+
+    // Create a new node without a file.
+    $nid = $this->uploadNodeFiles([], $field_name, $type_name);
+
+    // Make sure the "upload destination" radio input is on the page.
+    $this->drupalGet('node/' . $nid . '/edit');
+    $fields = $this->xpath('//input[@type="radio" and @name="' . $field_name . '[0][uri_scheme]" and @value="public"]');
+    $this->assertCount(1, $fields, 'Upload destination radio "public" input exists.');
+    $fields = $this->xpath('//input[@type="radio" and @name="' . $field_name . '[0][uri_scheme]"and @value="private" and @checked]');
+    $this->assertCount(1, $fields, 'Upload destination radio "private" exists and is default (the field storage setting).');
+  }
+
+  /**
+   * Tests using the upload destination toggle on a file field.
+   */
+  public function testUploadDestination() {
+    // Create a file field with uri_scheme_field, default uri_scheme to private.
+    $field_name = strtolower($this->randomMachineName());
+    $type_name = 'article';
+    $field_storage_settings = [
+      'uri_scheme_field' => '1',
+      'uri_scheme' => 'private',
+      'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
+    ];
+    $this->createFileField($field_name, 'node', $type_name, $field_storage_settings);
+
+    // Create a new node without a file.
+    $nid = $this->uploadNodeFiles([], $field_name, $type_name);
+
+    // Upload a file with the upload destination set to public.
+    $this->drupalGet('node/' . $nid . '/edit');
+    // Create a new file entity.
+    $file = File::create([
+      'uid' => 1,
+      'filename' => 'drupal.txt',
+      'uri' => 'temporary://drupal.txt',
+      'filemime' => 'text/plain',
+      'status' => FILE_STATUS_PERMANENT,
+    ]);
+    file_put_contents($file->getFileUri(), 'hello world');
+    $file->save();
+    $this->uploadNodeFileWithSettings($file, $field_name, $nid, ['uri_scheme' => 'public']);
+
+    // Test if the newly added file is saved in public://.
+    $path = file_create_url('public://');
+    $fields = $this->cssSelect('.field--name-' . $field_name . ' .field__item a');
+    $this->assertContains($path, $fields[0]->getAttribute('href'), 'File was uploaded to public file system.');
+
+    // Now change the upload location.
+    $this->drupalGet('node/' . $nid . '/edit');
+    $edit = [];
+    $name = $field_name . '[0][uri_scheme]';
+    $edit[$name] = 'private';
+    $this->drupalPostForm("node/$nid/edit", $edit, t('Save'));
+
+    // Test if the re-saved file is saved in private://.
+    // Dummy needed because '' is not allowed as route parameter.
+    $path = str_replace('dummy', '', file_create_url('private://dummy'));
+    $fields = $this->cssSelect('.field--name-' . $field_name . ' .field__item a');
+    $this->assertContains($path, $fields[0]->getAttribute('href'), 'File was uploaded to private file system.');
+  }
+
+  /**
+   * Uploads a file with extra settings to a node.
+   *
+   * @param \Drupal\file\FileInterface $file
+   *   The file to be uploaded.
+   * @param string $field_name
+   *   The name of the field on which the files should be saved.
+   * @param int $nid
+   *   A numeric node id to upload files to an existing node.
+   * @param array $settings
+   *   Additional values when a new file is uploaded.
+   *
+   * @return int
+   *   The node id.
+   */
+  public function uploadNodeFileWithSettings($file, $field_name, $nid, array $settings = []) {
+    $node_storage = $this->container->get('entity_type.manager')->getStorage('node');
+    $node = $node_storage->load($nid);
+    $edit = [];
+
+    // Attach files to the node.
+    $field_storage = FieldStorageConfig::loadByName('node', $field_name);
+    // File input name depends on number of files already uploaded.
+    $field_num = count($node->{$field_name});
+    $name = 'files[' . $field_name . "_$field_num]";
+    if ($field_storage->getCardinality() !== 1) {
+      $name .= '[]';
+    }
+    $file_path = $this->container->get('file_system')->realpath($file->getFileUri());
+    $edit[$name] = $file_path;
+
+    foreach ($settings as $key => $setting) {
+      $name = $field_name . '[' . $field_num . '][' . $key . ']';
+      $edit[$name] = $setting;
+    }
+
+    $this->drupalPostForm("node/$nid/edit", $edit, t('Save'));
+
+    return $nid;
+  }
+
+}
diff --git a/core/profiles/standard/config/install/field.storage.node.field_image.yml b/core/profiles/standard/config/install/field.storage.node.field_image.yml
index e4da708546..5e3f9cf655 100644
--- a/core/profiles/standard/config/install/field.storage.node.field_image.yml
+++ b/core/profiles/standard/config/install/field.storage.node.field_image.yml
@@ -11,6 +11,7 @@ entity_type: node
 type: image
 settings:
   uri_scheme: public
+  uri_scheme_field: false
   default_image:
     uuid: null
     alt: ''
diff --git a/core/profiles/standard/config/install/field.storage.user.user_picture.yml b/core/profiles/standard/config/install/field.storage.user.user_picture.yml
index 82536283ef..5357b0a891 100644
--- a/core/profiles/standard/config/install/field.storage.user.user_picture.yml
+++ b/core/profiles/standard/config/install/field.storage.user.user_picture.yml
@@ -11,6 +11,7 @@ entity_type: user
 type: image
 settings:
   uri_scheme: public
+  uri_scheme_field: false
   default_image:
     uuid: null
     alt: ''
