diff --git a/core/modules/migrate/src/Plugin/migrate/destination/EntityFile.php b/core/modules/migrate/src/Plugin/migrate/destination/EntityFile.php
index 77b471f..0128043 100644
--- a/core/modules/migrate/src/Plugin/migrate/destination/EntityFile.php
+++ b/core/modules/migrate/src/Plugin/migrate/destination/EntityFile.php
@@ -7,12 +7,16 @@
 
 namespace Drupal\migrate\Plugin\migrate\destination;
 
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Entity\EntityStorageInterface;
+use Drupal\Core\File\FileSystemInterface;
+use Drupal\Core\StreamWrapper\LocalStream;
+use Drupal\Core\StreamWrapper\StreamWrapperManagerInterface;
 use Drupal\migrate\Entity\MigrationInterface;
-use Drupal\migrate\Plugin\MigratePluginManager;
 use Drupal\migrate\Row;
 use Drupal\migrate\MigrateException;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Every migration that uses this destination must have an optional
@@ -25,9 +29,19 @@
 class EntityFile extends EntityContentBase {
 
   /**
+   * @var \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface
+   */
+  protected $streamWrapperManager;
+
+  /**
+   * @var \Drupal\Core\File\FileSystemInterface
+   */
+  protected $fileSystem;
+
+  /**
    * {@inheritdoc}
    */
-  public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EntityStorageInterface $storage, array $bundles, EntityManagerInterface $entity_manager) {
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EntityStorageInterface $storage, array $bundles, EntityManagerInterface $entity_manager, StreamWrapperManagerInterface $stream_wrappers, FileSystemInterface $file_system) {
     $configuration += array(
       'source_base_path' => '',
       'source_path_property' => 'filepath',
@@ -36,6 +50,27 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition
       'urlencode' => FALSE,
     );
     parent::__construct($configuration, $plugin_id, $plugin_definition, $migration, $storage, $bundles, $entity_manager);
+
+    $this->streamWrapperManager = $stream_wrappers;
+    $this->fileSystem = $file_system;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration = NULL) {
+    $entity_type = static::getEntityTypeId($plugin_id);
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $migration,
+      $container->get('entity.manager')->getStorage($entity_type),
+      array_keys($container->get('entity.manager')->getBundleInfo($entity_type)),
+      $container->get('entity.manager'),
+      $container->get('stream_wrapper_manager'),
+      $container->get('file_system')
+    );
   }
 
   /**
@@ -44,52 +79,141 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition
   public function import(Row $row, array $old_destination_id_values = array()) {
     $file = $row->getSourceProperty($this->configuration['source_path_property']);
     $destination = $row->getDestinationProperty($this->configuration['destination_path_property']);
+    $source = $this->configuration['source_base_path'] . $file;
 
-    // We check the destination to see if this is a temporary file. If it is
-    // then we do not prepend the source_base_path because temporary files are
-    // already absolute.
-    $source = $this->isTempFile($destination) ? $file : $this->configuration['source_base_path'] . $file;
-
-    $dirname = drupal_dirname($destination);
-    if (!file_prepare_directory($dirname, FILE_CREATE_DIRECTORY)) {
-      throw new MigrateException(t('Could not create directory %dirname', array('%dirname' => $dirname)));
+    // Ensure the source file exists, if it's a local URI or path.
+    if ($this->isLocalUri($source) && !file_exists($source)) {
+      throw new MigrateException(SafeMarkup::format('File %source does not exist.', ['%source' => $source]));
     }
 
     // If the start and end file is exactly the same, there is nothing to do.
-    if (drupal_realpath($source) === drupal_realpath($destination)) {
+    if ($this->isLocationUnchanged($source, $destination)) {
       return parent::import($row, $old_destination_id_values);
     }
 
-    $replace = FILE_EXISTS_REPLACE;
-    if (!empty($this->configuration['rename'])) {
-      $entity_id = $row->getDestinationProperty($this->getKey('id'));
-      if (!empty($entity_id) && ($entity = $this->storage->load($entity_id))) {
-        $replace = FILE_EXISTS_RENAME;
+    $replace = $this->getOverwriteMode($row);
+    $success = $this->writeFile($source, $destination, $replace);
+    if (!$success) {
+      $dir = $this->getDirectory($destination);
+      if (file_prepare_directory($dir, FILE_CREATE_DIRECTORY)) {
+        $success = $this->writeFile($source, $destination, $replace);
+      }
+      else {
+        throw new MigrateException(SafeMarkup::format('Could not create directory %dir', ['%dir' => $dir]));
       }
     }
 
+    if ($success) {
+      return parent::import($row, $old_destination_id_values);
+    }
+    else {
+      throw new MigrateException(SafeMarkup::format('File %source could not be copied to %destination.', ['%source' => $source, '%destination' => $destination]));
+    }
+  }
+
+  /**
+   * Tries to move or copy a file.
+   *
+   * @param string $source
+   *  The source path or URI.
+   * @param string $destination
+   *  The destination path or URI.
+   * @param integer $replace
+   *  FILE_EXISTS_REPLACE (default) or FILE_EXISTS_RENAME.
+   *
+   * @return boolean
+   *  TRUE on success, FALSE on failure.
+   */
+  protected function writeFile($source, $destination, $replace = FILE_EXISTS_REPLACE) {
     if ($this->configuration['move']) {
-      $copied = file_unmanaged_move($source, $destination, $replace);
+      return (boolean) file_unmanaged_move($source, $destination, $replace);
     }
     else {
-      // Determine whether we can perform this operation based on overwrite rules.
-      $original_destination = $destination;
       $destination = file_destination($destination, $replace);
-      if ($destination === FALSE) {
-        throw new MigrateException(t('File %file could not be copied because a file by that name already exists in the destination directory (%destination)', array('%file' => $source, '%destination' => $original_destination)));
-      }
       $source = $this->urlencode($source);
-      $copied = @copy($source, $destination);
+      return @copy($source, $destination);
     }
-    if ($copied) {
-      return parent::import($row, $old_destination_id_values);
+  }
+
+  /**
+   * Determines how to handle file conflicts.
+   *
+   * @param \Drupal\migrate\Row $row
+   *
+   * @return integer
+   *  Either FILE_EXISTS_REPLACE (default) or FILE_EXISTS_RENAME, depending
+   *  on the current configuration.
+   */
+  protected function getOverwriteMode(Row $row) {
+    if (!empty($this->configuration['rename'])) {
+      $entity_id = $row->getDestinationProperty($this->getKey('id'));
+      if ($entity_id && ($entity = $this->storage->load($entity_id))) {
+        return FILE_EXISTS_RENAME;
+      }
+    }
+    return FILE_EXISTS_REPLACE;
+  }
+
+  /**
+   * Returns the directory component of a URI or path.
+   *
+   * For URIs like public://foo.txt, the full physical path of public://
+   * will be returned, since a scheme by itself will trip up certain file
+   * API functions (such as file_prepare_directory()).
+   *
+   * @param string $uri
+   *  The URI or path.
+   *
+   * @return false|string
+   */
+  protected function getDirectory($uri) {
+    $dir = $this->fileSystem->dirname($uri);
+    if (substr($dir, -3) == '://') {
+      return $this->fileSystem->realpath($dir);
+    }
+    else {
+      return $dir;
+    }
+  }
+
+  /**
+   * Returns if the source and destination URIs represent identical paths.
+   * If either URI is a remote stream, will return FALSE.
+   *
+   * @param string $source
+   *  The source URI.
+   * @param string $destination
+   *  The destination URI.
+   *
+   * @return boolean
+   */
+  protected function isLocationUnchanged($source, $destination) {
+    if ($this->isLocalUri($source) && $this->isLocalUri($destination)) {
+      return $this->fileSystem->realpath($source) === $this->fileSystem->realpath($destination);
     }
     else {
-      throw new MigrateException(t('File %source could not be copied to %destination.', array('%source' => $source, '%destination' => $destination)));
+      return FALSE;
     }
   }
 
   /**
+   * Returns if the given URI or path is considered local.
+   *
+   * A URI or path is considered local if it either has no scheme component,
+   * or the scheme is implemented by a stream wrapper which extends
+   * \Drupal\Core\StreamWrapper\LocalStream.
+   *
+   * @param string $uri
+   *  The URI or path to test.
+   *
+   * @return boolean
+   */
+  protected function isLocalUri($uri) {
+    $scheme = $this->fileSystem->uriScheme($uri);
+    return $scheme === FALSE || $this->streamWrapperManager->getViaScheme($scheme) instanceof LocalStream;
+  }
+
+  /**
    * Urlencode all the components of a remote filename.
    *
    * @param string $filename
@@ -114,18 +238,4 @@ protected function urlencode($filename) {
     return $filename;
   }
 
-  /**
-   * Check if a file is a temp file.
-   *
-   * @param string $file
-   *   The destination file path.
-   *
-   * @return bool
-   *   TRUE if the file is temporary otherwise FALSE.
-   */
-  protected function isTempFile($file) {
-    $tmp = 'temporary://';
-    return substr($file, 0, strlen($tmp)) === $tmp;
-  }
-
 }
diff --git a/core/modules/migrate/tests/src/Unit/destination/EntityFileTest.php b/core/modules/migrate/tests/src/Unit/destination/EntityFileTest.php
index 731c722..ae818b8 100644
--- a/core/modules/migrate/tests/src/Unit/destination/EntityFileTest.php
+++ b/core/modules/migrate/tests/src/Unit/destination/EntityFileTest.php
@@ -7,26 +7,27 @@
 
 namespace Drupal\Tests\migrate\Unit\destination;
 
+use Drupal\Core\Site\Settings;
 use Drupal\migrate\Row;
 use Drupal\migrate\Plugin\migrate\destination\EntityFile;
-use Drupal\simpletest\KernelTestBase;
 use Drupal\Core\Entity\ContentEntityInterface;
 use Drupal\entity_test\Entity\EntityTest;
 use Drupal\migrate\MigrateException;
+use Drupal\simpletest\WebTestBase;
 
 /**
  * Tests the entity file destination plugin.
  *
  * @group migrate
  */
-class EntityFileTest extends KernelTestBase {
+class EntityFileTest extends WebTestBase {
 
   /**
    * Modules to install.
    *
    * @var array
    */
-  public static $modules = array('system', 'entity_test', 'user');
+  public static $modules = array('system', 'entity_test', 'user', 'file');
 
   /**
    * @var \Drupal\Tests\migrate\Unit\destination\TestEntityFile $destination
@@ -39,8 +40,9 @@ class EntityFileTest extends KernelTestBase {
   public function setUp() {
     parent::setUp();
     $this->destination = new TestEntityFile([]);
+    $this->destination->streamWrapperManager = \Drupal::getContainer()->get('stream_wrapper_manager');
+    $this->destination->fileSystem = \Drupal::getContainer()->get('file_system');
 
-    $this->installConfig(['system']);
     file_put_contents('/tmp/test-file.jpg', '');
   }
 
@@ -70,6 +72,8 @@ protected function localFileDataProvider() {
       [['filepath' => 'core/modules/simpletest/files/image-test.jpg'], 'public://file1.jpg', TRUE, DRUPAL_ROOT . '/'],
       // Test a temporary file using an absolute path.
       [['filepath' => '/tmp/test-file.jpg'], 'temporary://test.jpg', TRUE, ''],
+      // Test a temporary file using a relative path.
+      [['filepath' => 'test-file.jpg'], 'temporary://core/modules/simpletest/files/test.jpg', TRUE, '/tmp/'],
       // Test a remote path to local.
       [['filepath' => 'core/modules/simpletest/files/image-test.jpg'], 'public://remote-file.jpg', TRUE, $base_url . '/'],
       // Test a remote path to local inside a folder that doesn't exist.
@@ -77,17 +81,118 @@ protected function localFileDataProvider() {
     ];
   }
 
-
   /**
    * Test that non-existent files throw an exception.
    */
   public function testNonExistentSourceFile() {
+    $destination = '/non/existent/file';
     try {
-      $this->doImport(['filepath' => '/none/existent/file'], 'public://wontmatter.jpg');
+      // If this test passes, doImport() will raise a MigrateException and
+      // we'll never reach fail().
+      $this->doImport(['filepath' => $destination], 'public://wontmatter.jpg');
+      $this->fail('Expected Drupal\migrate\MigrateException when importing ' . $destination);
+    }
+    catch (MigrateException $e) {
+      $this->assertIdentical($e->getMessage(), 'File <em class="placeholder">' . $destination . '</em> does not exist.');
     }
-    catch (MigrateException $e) {}
-    $destination = '/none/existent/file';
-    $this->assertIdentical($e->getMessage(), 'File <em class="placeholder">' . $destination . '</em> could not be copied to <em class="placeholder">public://wontmatter.jpg</em>.');
+  }
+
+  /**
+   * Tests various invocations of the writeFile() method.
+   */
+  public function testWriteFile() {
+    $plugin = $this->destination;
+    touch('temporary://baz.txt');
+
+    // Moving an actual file should return TRUE.
+    $plugin->configuration['move'] = TRUE;
+    $this->assertTrue($plugin->writeFile('temporary://baz.txt', 'public://foo.txt'));
+
+    // Trying to move a non-existent file shold return FALSE.
+    $this->assertFalse($plugin->writeFile('temporary://invalid.txt', 'public://invalid.txt'));
+
+    // Copying over a file that already exists should replace the existing file.
+    $plugin->configuration['move'] = FALSE;
+    touch('temporary://baz.txt');
+    $this->assertTrue($plugin->writeFile('temporary://baz.txt', 'public://foo.txt'));
+    // Copying over a file that already exists should rename the resulting file
+    // if FILE_EXISTS_RENAME is specified.
+    $plugin->writeFile('temporary://baz.txt', 'public://foo.txt', FILE_EXISTS_RENAME);
+    $this->assertTrue(file_exists('public://foo_0.txt'));
+
+    // Trying to copy a non-existent file should return FALSE.
+    $this->assertFalse($plugin->writeFile('temporary://invalid.txt', 'public://invalid.txt'));
+  }
+
+  /**
+   * Tests various invocations of the getOverwriteMode() method.
+   */
+  public function testGetOverwriteMode() {
+    $row = new Row([], []);
+    $plugin = $this->destination;
+    // If the plugin is not configured to rename the destination file, we should
+    // always get FILE_EXISTS_REPLACE.
+    $this->assertIdentical(FILE_EXISTS_REPLACE, $plugin->getOverwriteMode($row));
+
+    // When the plugin IS configured to rename the destination file, it should
+    // return FILE_EXISTS_RENAME if the destination entity already exists,
+    // and FILE_EXISTS_REPLACE otherwise.
+    $plugin->configuration['rename'] = TRUE;
+    $plugin->storage = \Drupal::entityManager()->getStorage('file');
+    /** @var \Drupal\file\FileInterface $file */
+    $file = $plugin->storage->create();
+    touch('public://foo.txt');
+    $file->setFileUri('public://foo.txt');
+    $file->save();
+    $row->setDestinationProperty($plugin->storage->getEntityType()->getKey('id'), $file->id());
+    $this->assertIdentical(FILE_EXISTS_RENAME, $plugin->getOverwriteMode($row));
+    unlink('public://foo.txt');
+  }
+
+  /**
+   * Tests various invocations of the getDirectory() method.
+   */
+  public function testGetDirectory() {
+    $plugin = $this->destination;
+    $this->assertEqual('public://foo', $plugin->getDirectory('public://foo/baz.txt'));
+    $this->assertEqual('/path/to', $plugin->getDirectory('/path/to/foo.txt'));
+    // A directory like public:// (no path) needs to resolve to a physical path.
+    $fs = \Drupal::getContainer()->get('file_system');
+    $this->assertEqual($fs->realpath(Settings::get('file_public_path')), $plugin->getDirectory('public://foo.txt'));
+  }
+
+  /**
+   * Tests various invocations of the isLocationUnchanged() method.
+   */
+  public function testIsLocationUnchanged() {
+    $plugin = $this->destination;
+    $public_dir = Settings::get('file_public_path');
+
+    // Due to the limitations of realpath(), the source file must exist.
+    touch('public://foo.txt');
+    $this->assertTrue($plugin->isLocationUnchanged($public_dir . '/foo.txt', 'public://foo.txt'));
+    unlink('public://foo.txt');
+
+    // In the context of the test, temporary:// will not map to /tmp, so
+    // isLocationUnchanged() should return FALSE.
+    touch('/tmp/foo.txt');
+    $this->assertFalse($plugin->isLocationUnchanged('/tmp/foo.txt', 'temporary://foo.txt'));
+    unlink('/tmp/foo.txt');
+  }
+
+  /**
+   * Tests various inovcations of the isLocalUri() method.
+   */
+  public function testIsLocalUri() {
+    $plugin = $this->destination;
+    $this->assertTrue($plugin->isLocalUri('public://foo.txt'));
+    $this->assertTrue($plugin->isLocalUri('public://path/to/foo.txt'));
+    $this->assertTrue($plugin->isLocalUri('temporary://foo.txt'));
+    $this->assertTrue($plugin->isLocalUri('temporary://path/to/foo.txt'));
+    $this->assertTrue($plugin->isLocalUri('foo.txt'));
+    $this->assertTrue($plugin->isLocalUri('/path/to/files/foo.txt'));
+    $this->assertTrue($plugin->isLocalUri('relative/path/to/foo.txt'));
+    $this->assertFalse($plugin->isLocalUri('http://www.example.com/foo.txt'));
   }
 
   /**
@@ -132,6 +237,21 @@ class TestEntityFile extends EntityFile {
    */
   public $configuration;
 
+  /**
+   * @var \Drupal\Core\Entity\EntityStorageInterface
+   */
+  public $storage;
+
+  /**
+   * @var \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface
+   */
+  public $streamWrapperManager;
+
+  /**
+   * @var \Drupal\Core\File\FileSystemInterface
+   */
+  public $fileSystem;
+
   public function __construct($configuration) {
     $configuration +=  array(
       'source_base_path' => '',
@@ -157,4 +277,39 @@ protected function getEntity(Row $row, array $old_destination_id_values) {
    */
   protected function save(ContentEntityInterface $entity, array $old_destination_id_values = array()) {}
 
+  /**
+   * {@inheritdoc}
+   */
+  public function writeFile($source, $destination, $replace = FILE_EXISTS_REPLACE) {
+    return parent::writeFile($source, $destination, $replace);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getOverwriteMode(Row $row) {
+    return parent::getOverwriteMode($row);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDirectory($uri) {
+    return parent::getDirectory($uri);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isLocationUnchanged($source, $destination) {
+    return parent::isLocationUnchanged($source, $destination);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isLocalUri($uri) {
+    return parent::isLocalUri($uri);
+  }
+
 }
diff --git a/core/modules/migrate_drupal/src/Tests/d6/MigrateFileTest.php b/core/modules/migrate_drupal/src/Tests/d6/MigrateFileTest.php
index 91560a4..c067eb3 100644
--- a/core/modules/migrate_drupal/src/Tests/d6/MigrateFileTest.php
+++ b/core/modules/migrate_drupal/src/Tests/d6/MigrateFileTest.php
@@ -10,7 +10,6 @@
 use Drupal\Component\Utility\Random;
 use Drupal\migrate\MigrateExecutable;
 use Drupal\migrate\Tests\MigrateDumpAlterInterface;
-use Drupal\migrate_drupal\Tests\d6\MigrateDrupal6TestBase;
 use Drupal\Core\Database\Database;
 use Drupal\simpletest\TestBase;
 
@@ -64,6 +63,7 @@ public function testFiles() {
     $this->assertIdentical('39325', $file->getSize());
     $this->assertIdentical('public://image-1.png', $file->getFileUri());
     $this->assertIdentical('image/png', $file->getMimeType());
+
     // It is pointless to run the second half from MigrateDrupal6Test.
     if (empty($this->standalone)) {
       return;
