Drupal 6: How to Change Filename on Upload

I want to rewrite the file name when a user uploads a file. How on earth do you do that?

There appears to be very little documentation on this, so I present to you my solution, and an explanation of how I figured this out. Hopefully this will help a stranded Drupaler out there.

The Code Snippet

The following code will rewrite an uploaded file's filename from to 1234567890_<timestamp>, where "1234567890" is the timestamp.

Insert this into a custom module:

/**
 * Implementation of FileField's hook_file_update().
 *
 * This gets called after the file has been saved, and technically right AFTER writing to the database.
 * That's why we need to call drupal_write_record() to update the file record.
 * Too bad we can't use D7's file_save() :(
 */
function MYMODULE_file_insert($file) {
  // change filename to <timestamp>_<filename> for absolute uniqueness, and replace spaces with '_'
  $new_filename = $file->timestamp . '_' . str_replace(' ', '_', $file->filename);
  $new_filepath = dirname($file->destination) . '/' . $new_filename;
  // $file is passed by reference
  if (file_move($file, $new_filepath)) {
    // update files table with new file information
    drupal_write_record('files', $file, 'fid');
  };
}
/**
 * Implementation of FileField's hook_file_update().
 */
function MYMODULE_file_update(&$file) {
  if (!empty($file->fid)) {
    MYMODULE_file_delete($file);
    MYMODULE_file_insert($file);
  }
}
/**
 * Implementation of FileField's hook_file_delete().
 */
function MYMODULE_file_delete($file) {
    // add any special delete processing here
}

Replace "MYMODULE" with your module's name.

If you're not sure how to make a custom module, read this tutorial.

Example Usage

CCK FileField module must be installed. In my case, I'm using Video module's Uploadfield, but any Filefield (including ImageField) will do.

In this example, a user is prompted to upload a file, let's say Bear.wmv.

Bear.wmv

When they click "Upload", the file is actually saved as 1298410169_Bear.wmv, which is of the format <timestamp>_<filename>.

1298410169_Bear.wmv

How It Works

Seems simple enough, right? The thing is, FileField, by default, doesn't allow us to do this. Let's dig deeper into what's going on.

FileField works like this:

A CCK field is created that uses the AHAH uploader widget.

CCK Manage fields

This creates an AHAH Uploader form that saves the file in the "files" table.

files table

What else is happening? Let's add some fact-finding code to a custom module. If Devel module is enabled, we can use the dsm() function call to learn more.

The form is rebuilt when the file upload is returned. We can check this by implementing hook_form_alter() in a custom module:

/**
 * Implements hook_form_alter().
 */
function mymodule_form_alter(&$form, &$form_state, $form_id) {
  switch ($form_id) {
    case 'page_node_form':
      $form['#after_build'][] = '_mymodule_video_after_build';
      break;
  }
}
function _mymodule_video_after_build($form, &$form_state) {
  dsm($form);
  dsm($form_state);
  return $form; // don't forget return value!
}

This bit of code is adding an #after_build function to the form, which outputs $form and $form_state in a dsm() call.

We notice that when you upload a file by clicking "Upload", the dsm() messages display right above the File field.

dsm() messages

Expand the second array, then "values", and find the "file" field type that you set up, in this case "field_example_file". We then see all the values that are associated with the file.

field_example_file

It looks like "filename", "filepath", and "destination" are all the fields we want to modify. But how do we intercept FileField's upload process, and insert our modifications?

Turns out, there's actually some hooks that FileField module introduces that you can override. They're not available to view in the Drupal API, but we can find them if we search implementations in a contrib API like the Lullabot API. They are:

hook_file_insert()
hook_file_delete()
hook_file_update()

The documentation's pretty light. So, where to start? The FileField Meta module has a great example implementation that we can copy. We can examine it by using the Lullabot API or opening the file itself. Lullabot API links:

The code itself is located within the filefield/ module folder. If you look at filefield/filefield_meta/filefield_meta.module around Line 73, you'll see this:

filefield_meta.module

/**
 * Implementation of FileField's hook_file_insert().
 */
function filefield_meta_file_insert(&$file) {
  if (!empty($file->fid)) {
    filefield_meta($file);
    $record = array_merge($file->data, array('fid' => $file->fid));
    drupal_write_record('filefield_meta', $record);
  }
}

/**
 * Implementation of FileField's hook_file_update().
 */
function filefield_meta_file_update(&$file) {
  if (!empty($file->fid)) {
    filefield_meta_file_delete($file);
    filefield_meta_file_insert($file);
  }
}

/**
 * Implementation of FileField's hook_file_delete().
 */
function filefield_meta_file_delete($file) {
  db_query('DELETE FROM {filefield_meta} WHERE fid = %d', $file->fid);
}

Okay, that tells us something. filefield_meta_file_insert() is taking the $file object by reference and running it's own function, filefield_meta(), on it. That's where we'll want to add our modifications. Now, a tricky part is that hook_file_insert() actually is fired just AFTER the file is inserted. This means that the files table has already been written. That's why drupal_write_record() is called by the filefield_meta implementation. We're actually going to modify the file and then update the file record. Drupal 7 has a really neat hook called file_save() which unfortunately we can't use. So, we have to use drupal_write_record().

Let's add a dsm() right in our MYMODULE_file_insert(), this will show us what fields we have available.

function MYMODULE_file_insert($file) {
  // output file object
  dsm($file);
}

If you add a file again, you should see a third message at the top, which says (Object) stdClass. That's our file object. Expanding that show us all the field we have available:

file object

Since the file is already saved, we're going to call file_move() here. file_move() accepts a $file object by reference, and updates it.

Breaking down our implementation in the snippet:

$new_filename = $file->timestamp . '_' . str_replace(' ', '_', $file->filename);

We're taking the file object's timestamp and appending it to the filename. I added in an extra bit that replaces any space characters with underscores. This could be useful for, say, ImageField module, because some WYSIWYG uploaders don't like space characters.

$new_filepath = dirname($file->destination) . '/' . $new_filename;

This line takes the current directory that the file is sitting in (using PHP's dirname()) and appends the new filename to it.

Next we call file_move(). It accepts the file object by reference, and updates it accordingly. It also handles renaming the file if there's a duplicate, etc.

if (file_move($file, $new_filepath)) {
  // update files table with new file information
  drupal_write_record('files', $file, 'fid');
}

If the move was successful, we call drupal_write_record() to update the database.

At this point the file is properly moved and the database updated. And we're done!

file renamed

How to Only Modify Some Uploaded Files

This solution modifies every file upload. What if we only wanted to modify a specific CCK field? We can use the $file object's "source" value.

If you look at the example above, the source value for my file field is "field_example_file_0". So, we can add an IF statement to our code to only target uploads to that field.

function MYMODULE_file_insert($file) {
  if ($file->source == 'field_example_file_0') {
    // change filename to <timestamp>_<filename> for absolute uniqueness, and replace spaces with '_'
    $new_filename = $file->timestamp . '_' . str_replace(' ', '_', $file->filename);
    $new_filepath = dirname($file->destination) . '/' . $new_filename;
    // $file is passed by reference
    if (file_move($file, $new_filepath)) {
      // update files table with new file information
      drupal_write_record('files', $file, 'fid');
    };
  }
}

And that's all there is to it! I hope you found this write-up helpful. If there's any questions about my implementation, I'd be happy to answer them.

Comments

Mikael Brandin's picture

Great article! Just what I was looking for.

wjones's picture

Cool! I'm still surprised no one had written a post about this before... it seems really desirable.

Ron Williams's picture

There is a very useful module called FileField Paths that allows rewriting of filenames based on tokens on a per-field basis.

--
Ron Williams
http://www.lithicmedia.com/

Alex's picture

Awesome Work!
Thank you for this tutorial. I searched the last 3 weeks for this solution.

My main task was to scale the "original"-image down to 600x600, to safe space.

<?php
function MYMODULE_file_insert($file) {
   
   
$path = dirname($file->destination) . '/'.$file->filename;
   
dsm($path);
   
$width = 600;
   
$height = 600;
   
image_scale($path, $path, $width, $height);

   
//... tutorialcode ...
}
?>

richard d's picture

thanks. I'm just learning drupal and this info was helpful. FAPI is a pita for a noob like me.

Brian Shensky's picture

I'm thinking you just instructed me on to write a custom module that allows me to pass a file that was uploaded to a "slave" server along to a "master" server in a multi-node server cluster that uses one-way master-to-slave local-filesystem replication. (see: lsyncd and csync2)

How: Easy - write the hook_file_insert() code that checks the current server's IP and, if not the master server's IP, scp that file to the master, so it can replicate it "back" to ALL the other slave nodes.

Yeah, it's a little hacky, but it beats having to care for and feed a GlusterFS implementation all for the sake of an occasional file upload.

Anonymous's picture

Thanks for sharing the info,but I have to say that Software Tailor is always so helpful. They give us suppot both in technology and spirit.Please find Software Outsourcing HK

Brendon George's picture

Oh thank you so much for that code. I am an armature programmer. I was searching for this all over internet. I searched a lot in Google codes. I could not find anything there. But at last, I found it here. I thank you for posting about it.
more here

ram14's picture

Apple products are revered for its quality, precision and great design. SRSG started its operations as Apple technology partners in the year 1997.
Apple service center Mumbai
Apple product dealers in Kolkata
AVID pro tools dealers

lamountgoff's picture

You've shared an essential code that could help my project to be finish soon. Thanks a lot for the share. how to get more instagram followers

Add new comment