A brief explanation of this bug is in post #4

I'm trying to migrate files from myoldsite.com/gallery which has a 'data' folder and category numbered subfolders like:

/home/myoldsite/public_html/gallery/data/501/house.jpg
/home/myoldsite/public_html/gallery/data/501/plane.jpg
/home/myoldsite/public_html/gallery/data/504/house.jpg
/home/myoldsite/public_html/gallery/data/506/tree.png

The corresponding MySQL db 'photos' source table looks like:

| id | user id | cat | bigimage |
---------------------------------------------------------
| 1 | 32 | 501 | house.jpg |
| 2 | 68 | 501 | plane.jpg |
| 3 | 12 | 504 | house.jpg |
| 4 | 25 | 506 | tree.png |

I want to keep the images in their original folders, so I've FTP'd the 'data' folder to D7's sites/default/files, which now looks like:

sites/default/files/data/501/house.jpg
sites/default/files/data/501/plane.jpg
sites/default/files/data/504/house.jpg
sites/default/files/data/506/tree.png

Using MigrateFileFieldHandler I've set up a migration as

    $image_arguments = MigrateFileFieldHandler::arguments(null, 'file_link', FILE_EXISTS_ERROR);       
    $this->addFieldMapping('field_image', 'bigimage')
    ->arguments($image_arguments);

with prepareRow

    public function prepareRow($current_row) {
      $current_row->bigimage  = 'sites/default/files/data' . '/' . $current_row->cat . '/' . $current_row->bigimage;
    } 

file_link shows the original images OK, but it doesn't enable imagecache versions for 'preview/thumbnail etc' to be created. I take it file_link files aren't entities in Drupal, even if the files are in the sites/default/files folder?

Comments

StuartDH’s picture

Issue summary: View changes

.

StuartDH’s picture

Issue summary: View changes

.

StuartDH’s picture

Issue summary: View changes

.

StuartDH’s picture

Issue summary: View changes

.

StuartDH’s picture

Issue summary: View changes

.

StuartDH’s picture

Issue summary: View changes

.

StuartDH’s picture

Issue summary: View changes

.

StuartDH’s picture

Issue summary: View changes

.

StuartDH’s picture

Issue summary: View changes

.

StuartDH’s picture

Issue summary: View changes

.

StuartDH’s picture

Issue summary: View changes

.

StuartDH’s picture

Issue summary: View changes

.

StuartDH’s picture

Issue summary: View changes

.

StuartDH’s picture

Issue summary: View changes

.

StuartDH’s picture

Issue summary: View changes

.

StuartDH’s picture

Issue summary: View changes

.

StuartDH’s picture

Issue summary: View changes

.

StuartDH’s picture

Issue summary: View changes

.

StuartDH’s picture

Issue summary: View changes

.

StuartDH’s picture

Issue summary: View changes

.

StuartDH’s picture

Issue summary: View changes

.

StuartDH’s picture

Title: File link » Good to use file_link?

Does only file_copy create entities, or can file_move be used?

StuartDH’s picture

Title: copy folder structure bug » Good to use file_link?
Category: bug » support

All file_copy files are ending up in /sites/default/files/data/ without the 'cat' numbered subfolders, i.e.:

/sites/default/files/data/house.jpg
/sites/default/files/data/plane.jpg

I've tried using public function prepare, but it doesn't seem to help in setting the destination path

    public function prepare($node, stdClass $row) {
      $row->field_image = 'sites/default/files/data' . '/' . $row->cat . '/' . $row->bigimage;
    }

Is this a bug? It looks like file_copy isn't copying the complete source path to the destination path

StuartDH’s picture

Title: Good to use file_link? » Maintaining file_copy folder structure

MigrationFileFieldHandler doesn't seem to work as desired, so I've created a class just to get the files in Drupal as entities:

class photopostImageMigration extends Migration {

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

    $this->description = t('Photopost photo images');

    $this->map = new MigrateSQLMap(
      $this->machineName,
      array(
        'id' => array(
          'type' => 'int',
          'unsigned' => TRUE,
          'not null' => TRUE,
          'description' => 'photopost gallery photos',
         )
       ),
       MigrateDestinationFile::getKeySchema()
    );

    // Set the query to get the source object

    $query = Database::getConnection('default', 'for_gallery_migration')
      ->select('photos', 'p')
      ->fields('p', array(
        'id',
        'userid',
        'cat',
        'storeid',
        'storecat',
        'bigimage',
      ));

    $this->source = new MigrateSourceSQL(
      $query, 
      array('url' => t('The external url is computed in prepareRow.')),
      NULL,
      array('map_joinable' => FALSE));

// copy_file will fetch a copy of the file (e.g. over http)

    $this->destination = new MigrateDestinationFile(array(
           "copy_file" => true,
           "copy_destination" => "public://pictures/"
         ));

// Create and map the file's uri with prepareRow

      $this->addFieldMapping('uid', 'userid');

      $this->addFieldMapping('uri', 'bigimage');

// Unmigrated Destinations

    $this->addUnmigratedDestinations(array(
      'filemime',  // detected automatically - e.g image/png
      'timestamp', // current timestamp
    ));
  }

  public function prepareRow($row) {
    $row->bigimage = 'sites/default/files/data/' . $row->cat . '/' . $row->bigimage;
  }

  public function prepare($node, stdClass $row) {
    $row->uri = 'data/' . $row->cat . '/' . $row->bigimage;
  }
}

The above sources the files correctly, the destinations now include the 'cat' subfolders, and I can see the images in Media's view library etc, but the resulting uris are

sites/default/files/pictures/sites/default/files/data/501/house.jpg
sites/default/files/pictures/sites/default/files/data/501/plane.jpg

and I'd like to shorten them to just

sites/default/files/pictures/data/501/house.jpg
sites/default/files/pictures/data/501/plane.jpg

More importantly, I can't get the above files to map in a subsequent MigrateDestinationNode when using something like:

    $this->addFieldMapping('field_image', 'bigimage')
         ->sourceMigration('photopostImage'); 

with prepareRow

    public function prepareRow($row) {
      $row->bigimage = 'sites/default/files/sites/default/files/data/' . $row->cat . '/' . $row->bigimage;
   }

The files map to field_image (a field in my 'photo' content type), but the file destination changes to just sites/default/files/

any ideas?

StuartDH’s picture

I think I might have found the issue...

Using MigrateFileFieldHandler or mapping in MigrateDestinationNode, if you migrate a file into a content type's field_image field, the file is saved to the directory that's set in the field's destination settings - admin/structure/types/manage/photo/fields/field_image

All mapped files seem to flatten and adopt the above setting. If nothing has been set in the file destination's box the folder structure of the files is still overridden and all files flattened into the default /files folder.

Is it possible to change the field mapping in migrate to ignore the above settings when setting the destination for files?

StuartDH’s picture

Issue summary: View changes

.

StuartDH’s picture

Title: Maintaining file_copy folder structure » copy folder structure bug
Category: support » bug

I'm changing this to a bug as it doesn't look as though file field mappings are being created as expected

I've just done a quick check and it looks as though file_move creates the same result, i.e.

    $arguments = MigrateFileFieldHandler::arguments(null, 'file_move', FILE_EXISTS_RENAME);
    $this->addFieldMapping('field_image', 'bigimage')
         ->arguments($arguments);

using the same prepareRow as above

aendra’s picture

Title: Good to use file_link? » copy folder structure bug
Category: support » bug

+1 Subscribing

(Thanks StuartDH for pointing me towards this issue).

StuartDH’s picture

Issue summary: View changes

.

StuartDH’s picture

I'm trying to find a workaround for this by using entity tokens/taxonomy tokens/field tokens etc to see if the file directory of the field_image can be set by other fieldmappings (my 'cat' terms) during import.

So far, no luck. Has anyone else managed to get results this way?

mikeryan’s picture

Category: bug » feature
Status: Active » Closed (duplicate)

Consolidating with #1120136: Option to flatten or preserve file hierarchies on import - the broader issue is that both the file destination and the file field handler should allow you to specify whether or not to maintain the existing hierarchy. This will all be part of the effort in Migrate 2.4 to refactor file handling: #1240928: META: Refactoring of file destination/field handlers.

mototribe’s picture

Title: copy folder structure bug » thank you

thank you Stuart for posting all the examples, it really helped me with my photo migration!

Fidelix’s picture

Title: thank you » copy folder structure bug
aendra’s picture

@StuartDH -- Any idea how to create filefield entities within another entity? I ask because I'm trying to migrate my existing folder structure with filefieldhandler (which doesn't seem to work, nor will seem to work for the foreseeable future), but can't create entities directly -- I'm using the field collection field handler to populate field collections, within which are my image fields.

Any thoughts? Thanks!

StuartDH’s picture

Do you mean getting image fields to show in something like an Article content type?

If so, the nearest I got was manually copying a folder (with subfolder) then using file_link like post #1

The images showed up in the content type, but I couldn't seem to create thumbnails etc for them

aendra’s picture

@Stuart -- Great, thanks for the help! I ended up doing that, then using phpThumb to manually resize all the images. Here's the code I used, for anyone else needing to do this before the shiny redone file handling comes out in 2.4:

<?php
private function resize_thumbs($path) {
		$origPath = '/home/aendrew/d7/sites/default/files/' . $path;
		$large_path  = '/home/aendrew/d7/sites/default/files/styles/large/public/sites/default/files/' . $path;
		if (!is_dir(dirname($large_path))){
			mkdir(dirname($large_path), 0777, true);
		}
		
		$medium_path = '/home/aendrew/d7/sites/default/files/styles/medium/public/sites/default/files/' . $path;
		if (!is_dir(dirname($medium_path))){
			mkdir(dirname($medium_path), 0777, true);
		}
		
		$square_thumbnail_path = '/home/aendrew/d7/sites/default/files/styles/square_thumbnail/public/sites/default/files/' . $path;
		if (!is_dir(dirname($square_thumbnail_path))){
			mkdir(dirname($square_thumbnail_path), 0777, true);
		}
				
		$thumbnail_path = '/home/aendrew/d7/sites/default/files/styles/thumbnail/public/sites/default/files/' . $path;
		if (!is_dir(dirname($thumbnail_path))){
			mkdir(dirname($thumbnail_path), 0777, true);
		}		

		$path_stuff = pathinfo($origPath);
		$accepted_exts = array('jpeg', 'jpg', 'png', 'gif');
                 if (isset($path_stuff['extension'])) {
			if (array_search(strtolower($path_stuff['extension']), $accepted_exts)) {
				$options = array('resizeUp' => false, 'preserveTransparency' => true);
				try {
					if (!file_exists($large_path) || !file_exists($medium_path) || !file_exists($thumbnail_path) || !file_exists($square_thumbnail_path)) {
						unset($image);
						watchdog('glegacy_migrate', 'Now on: %path', array('%path' => $origPath), WATCHDOG_NOTICE);					
						$image = PhpThumbFactory::create($origPath, $options);
						if (!file_exists($large_path)) {
							$image = PhpThumbFactory::create($origPath, $options);
							@$image->resize(480, 480)->save($large_path); //large
							unset($image);							
						}
						if (!file_exists($medium_path)) {					
							$image = PhpThumbFactory::create($origPath, $options);
							@$image->resize(220, 220)->save($medium_path); //medium
							unset($image);							
						}
						if (!file_exists($thumbnail_path)){				
							$image = PhpThumbFactory::create($origPath, $options);							
							@$image->resize(100, 100)->save($thumbnail_path); //thumbnail
							unset($image);							
						}
						if (!file_exists($square_thumbnail_path)) {						
							$image = PhpThumbFactory::create($origPath, $options);
							@$image->adaptiveResize(180, 180)->save($square_thumbnail_path); //square_thumb				
							unset($image);							
						}

					}
				}				
				catch (Exception $e) {
					self::displayMessage('Problem with image at ' . $origPath);
				}

		} else { //extension not present
			watchdog('thumbnail_migrate', 'Extensionless image at %path', array('%path' => $origPath), WATCHDOG_NOTICE);
			return FALSE;
		}
}

public function prepareRow($current_row) {
if (!file_exists('/home/aendrew/d7/sites/default/files/' . $current_row->file )) { // my DB stores file paths as directory1/directory2/4-digit-year/file.jpg
			return false;
		} else {
			$this->resize_thumbs($current_row->file);
			$current_row->file = 'sites/default/files/' . $path;
		}
}
?>

(Not visible here: I included ThumbLib.inc.php in the constructor so it's available later on.)

Hope this helps someone!

(Edit 2012-04-14: fixed typo and changed the thumbnail creation routine to redo the phpthumb create method for each -- otherwise, square thumbnails try to resize the 100x100 thumbs and looks awful)

jamesrobertson’s picture

re post #13
@aendrew - been looking for a resize image function to use with Migrate. Just want to check before I spend too much time...

is the method...

$this->resize_thumbs($current_row->file);

referring to...

private function resize_thumbnails($path)

if so - is this just a typo?

aendra’s picture

@James -- Yes, that's totally a typo. Fixed in the above code; thanks!

jamesrobertson’s picture

Importing an image from your oldsite (on a different server) is made really easy with phpThumb (thanks to @aendrew for introducing it to me)

I am pulling database information and files from www.djmag.com NOT /home/djmag/www

In the main ...NodeMigration class I have created an external connection (have a look at cross-database migrations)

$query = Database::getConnection('default', 'djmag')
		         ->select('magazine', 's');

So database stuff comes from this connection

But I want to pull files as well - so I have created this file mapping (see the beer.inc example for a better explanation)

$arguments = MigrateFileFieldHandler::arguments(NULL, 'file_copy', FILE_EXISTS_RENAME);
		$this->addFieldMapping('field_image', 'images')
		     ->arguments($arguments);

and then I have created these few lines of code in the prepareRow() function

public function prepareRow($current_row) {
        require_once '/path/to/phpThumb/ThumbLib.inc.php';
  //  $current_row->image_name is '3327.jpg' in this particular case (have a look at http://www.djmag.com/images/thumb/3327.jpg)
		$orig_path = "http://www.djmag.com/images/thumb/" . $current_row->image_name;
		$save_path = "/home/djmag/www/drupal-7.12/sites/default/files/cover/djmag/thumb/" . $current_row->image_name;
//OMG how simple is this code below!
		$image = PhpThumbFactory::create($orig_path)
			->save($save_path)
			;
//$current_row->images below refers to the second argument $this->addFieldMapping('field_image', 'images') in the main class above.
		$current_row->images = $save_path;
		
		RETURN TRUE;

}

I really hope this helps people who don't want to copy files (in bulk) from their existing server to their new server in order to effect a migration. But want to be able to do it selectively and on the fly. There maybe many reasons for this... a) there is no need to, b) you may still be using the old server whilst the switch is being made, c) you may want the same information on two different servers...

I am new to Drupal and programming so please forgive some of my terms, but I hope this helps - I couldn't find information anywhere about this.

By the way (and drush will remind you of this) if you are using Migrate to pull info off another server remember to have the necessary migration tables on that other server - 'migtate_map_...' and 'migrate_message_...'

Lastly - thank you so much @mikeryan for this wonderful module

jamesrobertson’s picture

Issue summary: View changes

.