Migration of multi-value pictures to user fields
Something is really odd about your code $this-&gt;destination is a MigrateDestinationFile yet $this-&gt;map is trying to track a MigrateDestinationNode .... I've never done anything that way.

I would do the following:


Import Users from Wordpress to Drupal.
Use your CSV file of legacy UIDs and picture files to run an update of the (newly) created Drupal users and insert the images as needed.


I'm doing this now as we speak on a Document content type of which there are 120k entries, but only 4k have movie files associated with them so I feel a node revision is a better use the time to import stuff.

This is an example from some work in progress and will not run as-is but it should be a concrete example:

class PrezDocumentMediaMigration extends PrezMigration {

  public function __construct($arguments) {
    parent::__construct($arguments);

    $document_fields = array(
      // ... fields ...
    );
    $this-&gt;description = 'Update created Documents with audio and video from legacy DB. There are only ~5k of entries.';

    $query = Database::getConnection('default', 'legacy_migration')
      -&gt;select('papers', 'p')
      -&gt;fields('p', $document_fields)
      -&gt;condition('av', 0, '&lt;&gt;');
    // Additional pseudo fields to move data from legacy derived data to drupal.
    $query-&gt;addExpression('NULL', 'local_video');
    $query-&gt;addExpression('NULL', 'remote_video');
    $query-&gt;addExpression('NULL', 'local_audio');
    $query-&gt;addExpression('NULL', 'remote_audio');

    $options = array('track_changes' =&gt; 1);

    $count_query = Database::getConnection('default', 'legacy_migration')
      -&gt;select('papers', 'p')
      -&gt;condition('av', 0, '&lt;&gt;');
    $count_query-&gt;addExpression('COUNT(pid)', 'cnt');

    // We are updating existing content in Drupal.
    $this-&gt;systemOfRecord = Migration::DESTINATION;

    $this-&gt;source = new MigrateSourceSQL($query, $document_fields, $count_query, $options);
    $this-&gt;destination = new MigrateDestinationNode('documents');
    $this-&gt;map = new MigrateSQLMap($this-&gt;machineName,
                                   array(
                                     'pid' =&gt; array(
                                       'type'     =&gt; 'int',
                                       'unsigned' =&gt; FALSE,
                                       'not null' =&gt; TRUE,
                                       'description' =&gt; 'Unique content reference legacy document',
                                       'alias' =&gt; 'doc',
                                     )
                                   ),
                                   MigrateDestinationNode::getKeySchema()
    );

    // Migration mappings, the documentation for these mappings can be seen at:
    // https://www.drupal.org/node/1133448

    $migrator_user = user_load_by_name('PrezMigrator');

    // Some default values for sanity.
    $this-&gt;addFieldMapping('uid')
      -&gt;defaultValue($migrator_user-&gt;uid);
    $this-&gt;addFieldMapping('revision')
      -&gt;defaultValue(1);
    $this-&gt;addFieldMapping('log')
      -&gt;defaultValue('inserted related media from legacy DB on ' . date("l jS of F g:i A.", time()));
    $this-&gt;addFieldMapping('promote')-&gt;defaultValue(0);
    $this-&gt;addFieldMapping('sticky')-&gt;defaultValue(0);
    $this-&gt;addFieldMapping('revision_uid')-&gt;defaultValue($migrator_user-&gt;uid);

    // What legacy content are we talking about ?
    // also we want this legacy ID to actually lookup through 2 source migrations.
    $this-&gt;addFieldMapping('nid', 'id')-&gt;sourceMigration(array('PrezDietNonPresidents','PrezDietPresidents'));

    // Map media files these are multiple value fields. They can accept a PHP array of values.
    $this-&gt;addFieldMapping('field_docs_media_videos_local', 'local_video');
    $this-&gt;addFieldMapping('field_docs_media_videos_remote', 'remote_video');
    $this-&gt;addFieldMapping('field_docs_media_audo_local', 'local_audio');
    $this-&gt;addFieldMapping('field_docs_media_audio_remote', 'remote_audio');

    // Each of the above media fields will need the following additional subfields
    // Only 1 is provided as an example to keep this example shorter:
    $this-&gt;addFieldMapping('field_docs_media_videos_local:file_class')
      -&gt;defaultValue('MigrateFileUri');
    $this-&gt;addFieldMapping('field_docs_media_videos_local:language')
      -&gt;defaultValue(LANGUAGE_NONE);
    $this-&gt;addFieldMapping('field_docs_media_videos_local:preserve_files')
      -&gt;defaultValue(FALSE);
    $this-&gt;addFieldMapping('field_docs_media_videos_local:file_replace')
      -&gt;defaultValue(FILE_EXISTS_REPLACE);
    $this-&gt;addFieldMapping('field_docs_media_videos_local:source_dir')
      -&gt;defaultValue('/var/www/2015/presidency/drupal7-project/build/legacy/htdocs/www/media/');
    $this-&gt;addFieldMapping('field_docs_media_videos_local:urlencode')
      -&gt;defaultValue(FALSE);
    $this-&gt;addFieldMapping('field_docs_media_videos_local:alt')-&gt;defaultValue('');
    $this-&gt;addFieldMapping('field_docs_media_videos_local:title')
      -&gt;defaultValue('');


    // List fields not be migrated to destination.
    $this-&gt;addUnmigratedDestinations(
      array(
        // ... massive list of fields.
      ));

  }

  public function prepareRow($row) {
    // Always include this fragment at the beginning of every prepareRow()
    // implementation, so parent classes can ignore rows.
    if (parent::prepareRow($row) === FALSE) {
      return FALSE;
    }

    $youtube_format = 3;
    $quicktime_format = 2;
    $audio_format = 1;

    switch ($row-&gt;av) {
      case $youtube_format:
        $row-&gt;remote_video = $this-&gt; find_remote_video($row-&gt;medialink);
        break;
      case $quicktime_format:
        $row-&gt;local_video = $this-&gt;find_local_video($row-&gt;medialink);      
        break;
      case $audio_format:
        $row-&gt;local_audio = $this-&gt;find_local_audio($row-&gt;av, $row-&gt;medialink);        
      default:
        break;
    }
    return TRUE;
  }

  // http://stackoverflow.com/questions/5830387/how-to-find-all-youtube-video-ids-in-a-string-using-a-regex
  protected function find_remote_video($link) {
      $separator = '***';
    $text = preg_replace('~
        # Match non-linked youtube URL in the wild. (Rev:20130823)
        https?://         # Required scheme. Either http or https.
        (?:[0-9A-Z-]+\.)? # Optional subdomain.
        (?:               # Group host alternatives.
          youtu\.be/      # Either youtu.be,
        | youtube         # or youtube.com or
          (?:-nocookie)?  # youtube-nocookie.com
          \.com           # followed by
          \S*             # Allow anything up to VIDEO_ID,
          [^\w\s-]       # but char before ID is non-ID char.
        )                 # End host alternatives.
        ([\w-]{11})      # $1: VIDEO_ID is exactly 11 chars.
        (?=[^\w-]|$)     # Assert next char is non-ID or EOS.
        (?!               # Assert URL is not pre-linked.
          [?=&amp;+%\w.-]*    # Allow URL (query) remainder.
          (?:             # Group pre-linked alternatives.
            [\'"][^&lt;&gt;]*&gt;  # Either inside a start tag,
          | &lt;/a&gt;          # or inside &lt;a&gt; element text contents.
          )               # End recognized pre-linked alts.
        )                 # End negative lookahead assertion.
        [?=&amp;+%\w.-]*        # Consume any URL (query) remainder.
        ~ix',
                         '//www.youtube.com/embed/$1' . $separator,
                         $link);
    return explode($separator, $text);
  }

  // other "find" media methods that return filesystem paths and stuff depending on where the media
  // existed in the legacy system.
}