Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
<?php
/**
* Implements of hook_drush_command().
*/
function migration_filepath_drush_command() {
$items = array();
$items['migration_filepath'] = array(
'description' => "Move files to subdirectories",
'callback' => 'drush_migration_filepath',
// We are using options rather than arguments so the source can be omitted
// if needed (e.g. files are just directly under sites/default/files).
'options' => array(
'source' => array(
'description' => 'The directory under files where the to be relocated files are.',
'example_value' => 'field/image',
),
'target' => array(
'description' => 'The target directory where the files will be moved to and restructured.',
'example_value' => 'pictures',
),
),
'examples' => array(
'drush migration_filepath' => 'Moves all files located in the public file directory to YYYY/MM/DD style subdirectory.',
),
'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_FULL
);
return $items;
}
/**
* Command callback to perform the file migration.
*
* The two optional options are the source and target directories. By default
* assumes that these are under the default path, e.g. sites/default/files.
*/
function drush_migration_filepath() {
// Source directory to be cleaned up. All images in this directory will be relocated.
$source_directory = rtrim(drush_get_option('source', ''), '/');
// Directory to place the new structure under. If does not exist will be created.
$target_directory = rtrim(drush_get_option('target', ''), '/');
// Regular expression to find files in the source directory.
// For now assume public files only.
// public://field/image/imagefield_hENvtS.png
$extensions = array('jpeg', 'jpg', 'gif', 'png');
// Construct a expression to find images located in the source directory.
$file_pattern = "[^\/]*"; // Finds anything that does not contain "/", should be fine.
// Append the trailing slash for the regular expression.
// Note, in some instances drupal places under public:/// (three slashes)
// even when no folder specified. Reason for this is not known yet.
$source_pattern = $source_directory ? $source_directory . "\/" : '';
$regex = "^public:\/\/" . $source_pattern . "(" . $file_pattern . ")\.(" . implode($extensions, '|') . ")$";
// Query the database for files that match this pattern.
$filetypes = array('image/jpeg', 'image/jpg', 'image/gif', 'image/png');
$query = db_select('file_managed', 'f')
->condition('filemime', $filetypes , 'IN')
->condition('uri', $regex, 'REGEXP');
$total_count = $query->countQuery()->execute()->fetchField();
drush_print(dt('@count entries are to be moved.', array('@count' => $total_count)));
// Select the files to be moved.
$files = $query->fields('f', array('fid', 'filename', 'uri', 'timestamp'))
->execute()
->fetchAll();
$count = 1;
foreach ($files as $file) {
preg_match_all("/$regex/i", $file->uri, $matches); // Note, $file->filename can be the SAME for different uri-s!
$filename = $matches[1][0] . "." . $matches[2][0];
$old_file_wrapper = file_stream_wrapper_get_instance_by_uri($file->uri);
// If the file has already been moved, or does not exist in the filesystem, move on.
if (FALSE === ($status = $old_file_wrapper->url_stat($file->uri, STREAM_URL_STAT_QUIET))) {
drush_log("File entry in the database does not exist on the filesystem.", 'notice');
continue;
}
// Each file should go to the directory based on its timestamp.
$target_directory_for_file = $target_directory . '/' . generate_directory_name($file->timestamp);
// Construct a dummy URI for it so we can use the stream wrappers.
$new_directory = file_build_uri($target_directory_for_file);
$wrapper = file_stream_wrapper_get_instance_by_uri($new_directory);
// Make sure that the new directory exists.
$wrapper->mkdir($new_directory, 0755, TRUE);
// Construct the new directory.
$wrapper = file_stream_wrapper_get_instance_by_uri($file->uri);
$new_uri = file_build_uri($target_directory_for_file . '/' . $filename);
$wrapper->rename($file->uri, $new_uri);
$progress = round(($count / $total_count) * 100);
drush_print($progress . '%');
$query = db_update('file_managed')
->fields(array('uri' => $new_uri))
->condition('fid', $file->fid)
->execute();
$count++;
}
}
/**
* Generate the new file structure based on the timestamp.
*/
function generate_directory_name($timestamp) {
$year = date('Y', $timestamp);
$month = date('m', $timestamp);
$day = date('d', $timestamp);
return $year . '/' .$month . '/' . $day;
}
?>

Hi, thanks for this immensely helpful script.

I've created a modified version as it needed to generate directories based on file id and it is here https://gist.github.com/girishmuraly/9393882. A couple of extra options have also been added and a map file is generated - useful to create redirects.

--Cheers!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment