diff --git a/core/modules/migrate_drupal_ui/src/Batch/MigrateUpgradeImportBatch.php b/core/modules/migrate_drupal_ui/src/Batch/MigrateUpgradeImportBatch.php
index c894015cac..afba8709b7 100644
--- a/core/modules/migrate_drupal_ui/src/Batch/MigrateUpgradeImportBatch.php
+++ b/core/modules/migrate_drupal_ui/src/Batch/MigrateUpgradeImportBatch.php
@@ -111,13 +111,18 @@ public static function run($initial_ids, $config, &$context) {
     $definition = \Drupal::service('plugin.manager.migration')->getDefinition($migration_id);
     $configuration = [];
 
-    // @todo Find a way to avoid this in https://www.drupal.org/node/2804611.
+    // Set the source plugin constant, source_base_path, for all migrations with
+    // a file entity destination.
+    // @todo https://www.drupal.org/node/2804611.
+    //   Find a way to avoid having to set configuration here.
     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_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/CredentialForm.php b/core/modules/migrate_drupal_ui/src/Form/CredentialForm.php
index 4e8d621fe8..8900a50768 100644
--- a/core/modules/migrate_drupal_ui/src/Form/CredentialForm.php
+++ b/core/modules/migrate_drupal_ui/src/Form/CredentialForm.php
@@ -189,7 +189,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
       '#type' => 'textfield',
       '#title' => $this->t('Document root for private files'),
       '#default_value' => '',
-      '#description' => $this->t('To import private files from your current Drupal site, enter a local file directory containing your site (e.g. /var/www/docroot).'),
+      '#description' => $this->t('To import private files from your current Drupal site, enter a local file directory containing your site (e.g. /var/www/docroot). Leave blank to use the same value as Public files directory.'),
       '#states' => [
         'visible' => [
           ':input[name="version"]' => ['value' => '7'],
diff --git a/core/modules/migrate_drupal_ui/src/Form/ReviewForm.php b/core/modules/migrate_drupal_ui/src/Form/ReviewForm.php
index dfdbb43b4d..14ff137f5a 100644
--- a/core/modules/migrate_drupal_ui/src/Form/ReviewForm.php
+++ b/core/modules/migrate_drupal_ui/src/Form/ReviewForm.php
@@ -240,6 +240,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/MigrateUpgradeTestBase.php b/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeTestBase.php
index 3d2f053ce1..71ca6aae1f 100644
--- a/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeTestBase.php
+++ b/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeTestBase.php
@@ -293,6 +293,7 @@ protected function getCredentials() {
     }
     else {
       $edit['source_base_path'] = $this->getSourceBasePath();
+      $edit['source_private_file_path'] = $this->getSourcePrivateBasePath();
     }
     if (count($drivers) !== 1) {
       $edit['driver'] = $driver;
@@ -309,4 +310,33 @@ public function assertUserLogIn($uid, $pass) {
     $this->drupalLogin($user);
   }
 
+  /**
+   * Provides the source base path for private files for the credential form.
+   *
+   * @return string|null
+   *   The source base path.
+   */
+  protected function getSourcePrivateBasePath() {
+    return NULL;
+  }
+
+  /**
+   * 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) {
+      preg_match('/^(private|public|temporary):/', $file['uri'], $matches);
+      $scheme = $matches[1];
+      $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/d7/FilePathTest.php b/core/modules/migrate_drupal_ui/tests/src/Functional/d7/FilePathTest.php
new file mode 100644
index 0000000000..aa56cb19fe
--- /dev/null
+++ b/core/modules/migrate_drupal_ui/tests/src/Functional/d7/FilePathTest.php
@@ -0,0 +1,312 @@
+<?php
+
+namespace Drupal\Tests\migrate_drupal_ui\Functional\d7;
+
+use Drupal\Core\File\FileSystemInterface;
+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 service.
+   *
+   * @var \Drupal\Core\File\FileSystemInterface
+   */
+  protected $fs;
+
+  /**
+   * The base path to the source files on the destination site.
+   *
+   * @var string[]
+   */
+  protected $localDirectory = [];
+
+  /**
+   * The file scheme variables in the source database.
+   *
+   * These are 'file_private_path', 'file_public_path', and
+   * 'file_temporary_path',
+   *
+   * @var string[]
+   */
+  protected $sourceFileScheme = [];
+
+  /**
+   * {@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.
+   *
+   * @dataProvider providerTestFilePath
+   */
+  public function testFilePath(string $file_private_path, string $file_public_path, string $file_temporary_path, string $private, string $public, string $temporary) {
+    $this->sourceFileScheme['private'] = $file_private_path;
+    $this->sourceFileScheme['public'] = $file_public_path;
+    $this->sourceFileScheme['temporary'] = $file_temporary_path;
+
+    $this->localDirectory['private'] = $private;
+    $this->localDirectory['public'] = $public;
+    $this->localDirectory['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->submitForm([], 'Continue');
+    $this->submitForm($edits, 'Review upgrade');
+
+    // The migrations are now in store - remove all but the file migrations.
+    $store = \Drupal::service('tempstore.private')->get('migrate_drupal_ui');
+    $migration_array = array_intersect_key(
+      $store->get('migrations'),
+      array_flip(['d7_file', 'd7_file_private'])
+    );
+    $store->set('migrations', $migration_array);
+
+    // Perform the migrations.
+    $this->submitForm([], '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' => [
+        'sites/default/private',
+        'sites/default/files',
+        '/tmp',
+        '',
+        '',
+        '',
+      ],
+      'The private files are in a subdirectory' => [
+        'sites/default/private',
+        'sites/default/files',
+        '/tmp',
+        'abc',
+        '',
+        '',
+      ],
+      ' The public files are in a subdirectory' => [
+        'sites/default/private',
+        'sites/default/files',
+        '/tmp',
+        '',
+        'def',
+        '',
+      ],
+      'The private, public and temporary files are in separate subdirectories' => [
+        'private',
+        'files',
+        '/tmp',
+        'abc',
+        'def',
+        '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.
+   *
+   * For example:
+   *   The source site files_managed table.
+   *     uri: public://foo.txt
+   *     filename: foo.txt
+   *   The source site variable table.
+   *     file_public_path: sites/default/files
+   *   Local directory
+   *     /bar
+   *
+   * The resulting directory is /bar/sites/default/files/foo.txt.
+   */
+  protected function makeFiles() {
+    // Get file information from the source database.
+    foreach ($this->getManagedFiles() as $file) {
+      $this->assertSame(1, preg_match('/^(private|public|temporary):/', $file['uri'], $matches));
+      $scheme = $matches[1];
+      $path = $this->sourceFileScheme[$scheme] ?? '';
+      $filepath = implode('/', [
+        $this->getSourcePath($scheme),
+        $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.
+        $dir = $this->fs->dirname($filepath);
+        $this->fs->prepareDirectory($dir, FileSystemInterface:: CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS);
+        // Let's try that fopen again.
+        $source_file = @fopen($filepath, 'w');
+      }
+      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 = $this->localDirectory[$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() {
+  }
+
+}
