diff --git a/core/modules/migrate_drupal_ui/src/Batch/MigrateUpgradeImportBatch.php b/core/modules/migrate_drupal_ui/src/Batch/MigrateUpgradeImportBatch.php
index c9c19a5c30..ae3a44cf8c 100644
--- a/core/modules/migrate_drupal_ui/src/Batch/MigrateUpgradeImportBatch.php
+++ b/core/modules/migrate_drupal_ui/src/Batch/MigrateUpgradeImportBatch.php
@@ -111,13 +111,15 @@ public static function run($initial_ids, $config, &$context) {
     $definition = \Drupal::service('plugin.manager.migration')->getDefinition($migration_id);
     $configuration = [];
 
+    // Set the source plugin constant, source_base_path, for all migrations
+    // with a file entity destination.
     // @todo Find a way to avoid this in https://www.drupal.org/node/2804611.
     if ($definition['destination']['plugin'] === 'entity:file') {
-      // Make sure we have a single trailing slash.
-      if ($definition['source']['plugin'] === 'd7_file_private') {
-        $configuration['source']['constants']['source_base_path'] = rtrim($config['source_private_file_path'], '/') . '/';
-      }
-      $configuration['source']['constants']['source_base_path'] = rtrim($config['source_base_path'], '/') . '/';
+      // Use the private file path if the scheme property is set in the source
+      // plugin definition and is 'private' otherwise use the public file path.
+      $scheme = $definition['source']['scheme'] ?? NULL;
+      $base_path = ($scheme === 'private') ? $config['source_private_file_path'] : $config['source_base_path'];
+      $configuration['source']['constants']['source_base_path'] = rtrim($base_path, '/') . '/';
     }
 
     /** @var \Drupal\migrate\Plugin\Migration $migration */
diff --git a/core/modules/migrate_drupal_ui/src/Form/ReviewForm.php b/core/modules/migrate_drupal_ui/src/Form/ReviewForm.php
index 705aa24f9a..bb741af5c0 100644
--- a/core/modules/migrate_drupal_ui/src/Form/ReviewForm.php
+++ b/core/modules/migrate_drupal_ui/src/Form/ReviewForm.php
@@ -262,6 +262,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
    */
   public function submitForm(array &$form, FormStateInterface $form_state) {
     $config['source_base_path'] = $this->store->get('source_base_path');
+    $config['source_private_file_path'] = $this->store->get('source_private_file_path');
     $batch = [
       'title' => $this->t('Running upgrade'),
       'progress_message' => '',
diff --git a/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeExecuteTestBase.php b/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeExecuteTestBase.php
index 5eb14ccdd5..dbe67abc1b 100644
--- a/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeExecuteTestBase.php
+++ b/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeExecuteTestBase.php
@@ -69,6 +69,7 @@ public function testMigrateUpgradeExecute() {
     }
     else {
       $edit['source_base_path'] = $this->getSourceBasePath();
+      $edit['source_private_file_path'] = $this->getSourcePrivateBasePath();
     }
     if (count($drivers) !== 1) {
       $edit['driver'] = $driver;
@@ -168,4 +169,12 @@ public function testMigrateUpgradeExecute() {
     $this->assertMigrationResults($this->getEntityCountsIncremental(), $version);
   }
 
+  /**
+   * Gets the source base path for private files the concrete test.
+   *
+   * @return string
+   *   The source base path.
+   */
+  abstract protected function getSourcePrivateBasePath();
+
 }
diff --git a/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeTestBase.php b/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeTestBase.php
index 4b6754487c..75e1b2d785 100644
--- a/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeTestBase.php
+++ b/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeTestBase.php
@@ -260,4 +260,22 @@ protected function assertMigrationResults(array $expected_counts, $version) {
     }
   }
 
+  /**
+   * Checks public and private files are copied but not temporary files.
+   */
+  protected function assertFileMigrations() {
+    $fs = \Drupal::service('file_system');
+    $files = $this->getManagedFiles();
+    foreach ($files as $file) {
+      $scheme = preg_replace('/:.*/', '', $file['uri']);
+      $filepath = $fs->realpath($file['uri']);
+      if ($scheme === 'temporary') {
+        $this->assertFileNotExists($filepath);
+      }
+      else {
+        $this->assertFileExists($filepath);
+      }
+    }
+  }
+
 }
diff --git a/core/modules/migrate_drupal_ui/tests/src/Functional/d6/IdConflictTest.php b/core/modules/migrate_drupal_ui/tests/src/Functional/d6/IdConflictTest.php
index b629b45293..6ff58beff9 100644
--- a/core/modules/migrate_drupal_ui/tests/src/Functional/d6/IdConflictTest.php
+++ b/core/modules/migrate_drupal_ui/tests/src/Functional/d6/IdConflictTest.php
@@ -43,6 +43,12 @@ protected function getSourceBasePath() {
     return __DIR__ . '/files';
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  protected function getSourcePrivateBasePath() {
+  }
+
   /**
    * {@inheritdoc}
    */
diff --git a/core/modules/migrate_drupal_ui/tests/src/Functional/d6/NodeClassicTest.php b/core/modules/migrate_drupal_ui/tests/src/Functional/d6/NodeClassicTest.php
index 093944b893..687a15ef09 100644
--- a/core/modules/migrate_drupal_ui/tests/src/Functional/d6/NodeClassicTest.php
+++ b/core/modules/migrate_drupal_ui/tests/src/Functional/d6/NodeClassicTest.php
@@ -50,6 +50,12 @@ protected function getSourceBasePath() {
     return __DIR__ . '/files';
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  protected function getSourcePrivateBasePath() {
+  }
+
   /**
    * {@inheritdoc}
    */
diff --git a/core/modules/migrate_drupal_ui/tests/src/Functional/d6/Upgrade6Test.php b/core/modules/migrate_drupal_ui/tests/src/Functional/d6/Upgrade6Test.php
index 09e5fe815b..0032998595 100644
--- a/core/modules/migrate_drupal_ui/tests/src/Functional/d6/Upgrade6Test.php
+++ b/core/modules/migrate_drupal_ui/tests/src/Functional/d6/Upgrade6Test.php
@@ -65,6 +65,12 @@ protected function getSourceBasePath() {
     return __DIR__ . '/files';
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  protected function getSourcePrivateBasePath() {
+  }
+
   /**
    * {@inheritdoc}
    */
diff --git a/core/modules/migrate_drupal_ui/tests/src/Functional/d7/FilePathTest.php b/core/modules/migrate_drupal_ui/tests/src/Functional/d7/FilePathTest.php
new file mode 100644
index 0000000000..0d3158ffe0
--- /dev/null
+++ b/core/modules/migrate_drupal_ui/tests/src/Functional/d7/FilePathTest.php
@@ -0,0 +1,317 @@
+<?php
+
+namespace Drupal\Tests\migrate_drupal_ui\Functional\d7;
+
+use Drupal\Core\File\FileSystemInterface;
+use Drupal\migrate\MigrateException;
+use Drupal\Tests\migrate_drupal_ui\Functional\MigrateUpgradeTestBase;
+
+/**
+ * Tests the Drupal 7 public and private file migrations.
+ *
+ * To test file migrations both the public and private test source files are
+ * created in the temporary directory of the destination test site. Tests are
+ * done with the source files at the top level temporary directory and sub paths
+ * from that.
+ *
+ * @group migrate_drupal_ui
+ */
+class FilePathTest extends MigrateUpgradeTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $profile = 'testing';
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $defaultTheme = 'stark';
+
+  /**
+   * The file system.
+   *
+   * @var \Drupal\Core\File\FileSystem
+   */
+  protected $fs;
+
+  /**
+   * The file path variables in the source database.
+   *
+   * @var string[]
+   */
+  protected $basePath = [];
+
+  /**
+   * The file path variables in the source database.
+   *
+   * @var string[]
+   */
+  protected $variable = [];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $modules = [
+    'file',
+    'migrate',
+    'migrate_drupal',
+    'migrate_drupal_ui',
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp(): void {
+    parent::setUp();
+    $this->fs = \Drupal::service('file_system');
+    $this->loadFixture(drupal_get_path('module', 'migrate_drupal') . '/tests/fixtures/drupal7.php');
+  }
+
+  /**
+   * Executes all steps of migrations upgrade.
+   *
+   * @param string $file_private_path
+   *   The source database file_private_path value.
+   * @param string $file_public_path
+   *   The source database file_public_path value.
+   * @param string $file_temporary_path
+   *   The source database file_temporary_path value.
+   * @param string $private
+   *   The path to the source private files.
+   * @param string $public
+   *   The path to the source public files.
+   * @param string $temporary
+   *   The path to the source temporary files.
+   *
+   * @throws \Drupal\migrate\MigrateException
+   *
+   * @dataProvider providerTestFilePath
+   */
+  public function testFilePath($file_private_path, $file_public_path, $file_temporary_path, $private, $public, $temporary) {
+    $this->variable['private'] = $file_private_path;
+    $this->variable['public'] = $file_public_path;
+    $this->variable['temporary'] = $file_temporary_path;
+
+    $this->basePath['private'] = $private;
+    $this->basePath['public'] = $public;
+    $this->basePath['temporary'] = $temporary;
+
+    // Create the source files.
+    $this->makeFiles();
+
+    // Set the source db variables.
+    $this->sourceDatabase->update('variable')
+      ->fields(['value' => serialize($file_private_path)])
+      ->condition('name', 'file_private_path')
+      ->execute();
+    $this->sourceDatabase->update('variable')
+      ->fields(['value' => serialize($file_public_path)])
+      ->condition('name', 'file_public_path')
+      ->execute();
+    $this->sourceDatabase->update('variable')
+      ->fields(['value' => serialize($file_temporary_path)])
+      ->condition('name', 'file_temporary_path')
+      ->execute();
+
+    $connection_options = $this->sourceDatabase->getConnectionOptions();
+    $driver = $connection_options['driver'];
+    $connection_options['prefix'] = $connection_options['prefix']['default'];
+
+    // Use the driver connection form to get the correct options out of the
+    // database settings. This supports all of the databases we test against.
+    $drivers = drupal_get_database_types();
+    $form = $drivers[$driver]->getFormOptions($connection_options);
+    $connection_options = array_intersect_key($connection_options, $form + $form['advanced_options']);
+    $edit = [
+      $driver => $connection_options,
+      'version' => '7',
+    ];
+    if (count($drivers) !== 1) {
+      $edit['driver'] = $driver;
+    }
+    // Set the public and private base paths for the Credential Form.
+    $edit['source_private_file_path'] = $this->fs->realpath($this->getSourcePath('private'));
+    $edit['source_base_path'] = $this->fs->realpath($this->getSourcePath('public'));
+    $edits = $this->translatePostValues($edit);
+
+    // Start the upgrade.
+    $this->drupalGet('/upgrade');
+    $this->drupalPostForm(NULL, [], 'Continue');
+    $this->drupalPostForm(NULL, $edits, 'Review upgrade');
+
+    // The migrations are now in store - remove all but the file migrations.
+    $tempstore_private = \Drupal::service('tempstore.private');
+    $store = $tempstore_private->get('migrate_drupal_ui');
+    $all_migrations = $store->get('migrations');
+    $migration_array = array_intersect_key($all_migrations, [
+      'd7_file' => [],
+      'd7_file_private' => [],
+    ]);
+    $store->set('migrations', $migration_array);
+
+    // Perform the migrations.
+    $this->drupalPostForm(NULL, [], 'Perform upgrade');
+    $this->resetAll();
+    $this->assertFileMigrations();
+  }
+
+  /**
+   * Data provider of test dates for file path test.
+   *
+   * @return string[][]
+   *   An array of test data.
+   */
+  public function providerTestFilePath() {
+    return [
+      // All source base paths are at temporary://.
+      [
+        'file_private_path' => 'sites/default/private',
+        'file_public_path' => 'sites/default/files',
+        'file_temporary_path' => '/tmp',
+        'private' => '',
+        'public' => '',
+        'temporary' => '',
+      ],
+      // The private files are in a subdirectory.
+      [
+        'file_private_path' => 'sites/default/private',
+        'file_public_path' => 'sites/default/files',
+        'file_temporary_path' => '/tmp',
+        'private' => 'abc',
+        'public' => '',
+        'temporary' => '',
+      ],
+      // The public files are in a subdirectory.
+      [
+        'file_private_path' => 'sites/default/private',
+        'file_public_path' => 'sites/default/files',
+        'file_temporary_path' => '/tmp',
+        'private' => '',
+        'public' => 'def',
+        'temporary' => '',
+      ],
+      // The private and public files are in separate subdirectories.
+      [
+        'file_private_path' => 'private',
+        'file_public_path' => 'files',
+        'file_temporary_path' => '/tmp',
+        'private' => 'abc',
+        'public' => 'def',
+        'temporary' => 'xyz',
+      ],
+    ];
+  }
+
+  /**
+   * Creates files for the test.
+   *
+   * The source files are written to a subdirectory of the temporary files
+   * directory of the test sites. The subdirectory path always ends with the
+   * path to the relevant scheme as set in the source variable table.
+   */
+  protected function makeFiles() {
+    // Get file information from the source database.
+    $files = $this->getManagedFiles();
+    foreach ($files as $file) {
+      $scheme = preg_replace('/:.*/', '', $file['uri']);
+
+      $path = isset($this->variable[$scheme]) ? $this->variable[$scheme] : '';
+      $base_path = $this->getSourcePath($scheme) . '/' . $path;
+      $filepath = $base_path . '/' . $file['filename'];
+      // Create the file.
+      $source_file = @fopen($filepath, 'w');
+      if (!$source_file) {
+        // If fopen didn't work, make sure there's a writable directory in
+        // place.
+        $this->fs = \Drupal::service('file_system');
+        $dir = $this->fs->dirname($filepath);
+        if (!$this->fs->prepareDirectory($dir, FileSystemInterface:: CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS)) {
+          throw new MigrateException("Could not create or write to directory '$dir'");
+        }
+        // Let's try that fopen again.
+        $source_file = @fopen($filepath, 'w');
+        if (!$source_file) {
+          throw new MigrateException("Could not write to file '$filepath'");
+        }
+      }
+      fwrite($source_file, '42');
+    }
+  }
+
+  /**
+   * Gets the source base path for the Credential form.
+   *
+   * @param string $scheme
+   *   The file scheme.
+   */
+  public function getSourcePath($scheme) {
+    $base_path = isset($this->basePath[$scheme]) ? $this->basePath[$scheme] : '';
+    // Puts the source files in the site temp directory.
+    return $this->tempFilesDirectory . '/' . $base_path;
+  }
+
+  /**
+   * Gets the file data.
+   *
+   * @return string[][]
+   *   Data from the source file_managed table.
+   */
+  public function getManagedFiles() {
+    return [
+      [
+        'filename' => 'cube.jpeg',
+        'uri' => 'public://cube.jpeg',
+      ],
+      [
+        'filename' => 'ds9.txt',
+        'uri' => 'public://ds9.txt',
+      ],
+      [
+        'filename' => 'Babylon5.txt',
+        'uri' => 'private://Babylon5.txt',
+      ],
+      [
+        'filename' => 'TerokNor.txt',
+        'uri' => 'temporary://TerokNor.txt',
+      ],
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getEntityCounts() {
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getEntityCountsIncremental() {
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getAvailablePaths() {
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getMissingPaths() {
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getSourceBasePath() {
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getSourcePrivateBasePath() {
+  }
+
+}
diff --git a/core/modules/migrate_drupal_ui/tests/src/Functional/d7/IdConflictTest.php b/core/modules/migrate_drupal_ui/tests/src/Functional/d7/IdConflictTest.php
index 0fc8f13e47..da4e43e914 100644
--- a/core/modules/migrate_drupal_ui/tests/src/Functional/d7/IdConflictTest.php
+++ b/core/modules/migrate_drupal_ui/tests/src/Functional/d7/IdConflictTest.php
@@ -45,6 +45,12 @@ protected function getSourceBasePath() {
     return __DIR__ . '/files';
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  protected function getSourcePrivateBasePath() {
+  }
+
   /**
    * {@inheritdoc}
    */
diff --git a/core/modules/migrate_drupal_ui/tests/src/Functional/d7/Upgrade7Test.php b/core/modules/migrate_drupal_ui/tests/src/Functional/d7/Upgrade7Test.php
index 373ddd8efb..3f94cf66aa 100644
--- a/core/modules/migrate_drupal_ui/tests/src/Functional/d7/Upgrade7Test.php
+++ b/core/modules/migrate_drupal_ui/tests/src/Functional/d7/Upgrade7Test.php
@@ -67,6 +67,24 @@ protected function getSourceBasePath() {
     return __DIR__ . '/files';
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  protected function getSourcePrivateBasePath() {
+    return __DIR__ . '/files';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getFilenames() {
+    return [
+      'public' => ['cube.jpeg', 'ds9.txt'],
+      'private' => ['Babylon5.txt'],
+      'temporary' => ['TerokNor.txt'],
+    ];
+  }
+
   /**
    * {@inheritdoc}
    */
