4

I am aware that the File Paths module allows administrators to use tokens to set patterns for renaming files, but as of writing (Jan 2015) there are more than 100 bugs in the queue and no updates to the dev version in almost one year. (I also installed it and it and was unable to get it working.) So, I want to do this programmatically.

My goal is to rename all new user-uploaded image files on my Drupal 7 site to this pattern: [uid of logged in user][timestamp].extension.

Being able to rename them with the UID is very important because these files are often downloaded and exchanged (sent back and forth via email, without reference to Drupal), and we need an easy way to identify which user originally uploaded the file after it has been downloaded from the site.

I am using the Transliteration module. For inspiration, I also took a look at the File Entity Paths module, but it didn't provide me any insight. I further consulted this article on file-naming best practices from Acquia but it provides no suggestions for doing this programmatically.

4
+50

Assuming you are talking about the core file functionality, you can modify the filename in hook_file_presave() and modify the filename property.

function mymodule_file_presave($file) {
  $parts = pathinfo($file->filename);
  $file->filename = $file->uid . $file->timestamp . '.' . $parts['extension'];
}

Drupal will handle changing any other properties that need to be changed.

Some issues to be aware of:

  • Upload forms will expose the new name to the user after he clicks 'Upload'. It may be confusing to upload a file with a regular filename and see it changed to a string of numbers.

  • There is a note on the hook_file_presave documentation that the hook "doesn't distinguish between files created as a result of a copy or those created by an upload." If you do need to distinguish, check out the $file->source property or try calling menu_get_object() to figure out if the user is on a page with an upload form or not.

  • Consider using a dash between the uid and the timestamp for your naming convention. Your timestamps will all start with 1 (until 2033 anyway), and users with UIDs ending in 1 (e.g 11, 21, etc.) will make the filename confusing to read.

  • I'm also noticing that hook_file_presave will get called when the entity is updated. I still think it is a better bet than hook_file_insert(). – calebtr Feb 4 '15 at 16:55
  • BUT someone should add an answer making the case for hook_file_insert(). You might need to file_move and protect against an infinite loop somewhere, but it could solve the UX issue I mention above, and more. – calebtr Feb 5 '15 at 9:23
  • Great answer, and awesome of you to note the caveats. I tried it on my site, and although dpm() in file_presave() shows that $file->filename() is changed, and this is also reflected in the UI, when I actually check the server, the file is saved with the original file name (not the one specified in the hook). – Patrick Kenny Feb 5 '15 at 11:54
  • You can play with $file->destination and $file->uri if it matters what it is called on the filesystem. My understanding is that one is for filesystem access and the latter for the url. When I tested your requirement that it keep the name when downloaded and exchanged, $file->filename was the key to keeping that name. – calebtr Feb 5 '15 at 17:34
  • How did you download the file? When I right-click the image and choose "Save file as," the filesystem name is used. – Patrick Kenny Feb 6 '15 at 7:20
2

To rename files properly before they got saved (when a file being inserted or updated), use hook_file_presave() to change the filename in the table, or hook_file_insert() after a file has been added to the database, then use file_move() to actually move that uploaded file.

Here is an example using hook_file_insert:

/**
 * Implements hook_file_insert().
 */
function mymodule_file_insert($file) {

  if (strpos($file->filename, 'ShowImageXML.asp') !== FALSE) {
    $ext = strpos($filename, '.') !== FALSE ? drupal_strtolower(substr($file->filename, strrpos($file->filename, '.') + 1)) : '';

    $filename = sprintf("%d-%d.%s", $file->uid, $file->timestamp, $ext);
    $uri = 'public://' . $filename;
    watchdog('mymodule', 'File has been renamed from !old_file to !new_file.', array('!old_file' => $file->filename, '!new_file' => $filename), WATCHDOG_INFO);
    $file = file_move($file, $uri);
  }
}

If you need to do the same when a file has been added to the database, use hook_file_presave with similar code as above, but it's only for renaming file names in file_managed table, not in the file system.

1

Since Drupal manages the files usually you dont need to rely on file naming conventions to know what user uploaded a file and when. You can just look it up in the db.

If you really need to change the file paths: Option 1 An easy way to do this is by adding a computed field that looks the managed files the user uploaded and creates/manages copies by appling any pattern you may want. Then on node display you use the copy

Option 2 You alter the file upload widget or you use a module like the ones you propose to do this. This method requires no copies

Option 3 You go with a headless Drupal method. I.e. you dont use the node edit form but you create your own with Jquery, Angular, or anyother fronted framework. Then you create an API on the drupal side and you do whatever you want with the uploaded files before you persist/manage them and create a node. This method gives you suberb control and you can apply other custom functionality too (e.g. conversion of file types with imagick or custom cropping or annotations...) Howver if you go with this method make sure you take care of security issues (e.g. CSRF handling) that normally drupal takes care of automatically

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.