I have added a source code content type with filefield to upload user's file in node. However, when they edit their own content and upload file with the same filename the old one is not overwritten. How do I overwrite the old one with the new file?

Thanks

Comments

quicksketch’s picture

You can't directly "overwrite" an existing file, because FileField doesn't know if you're going to save the changes or not. Say you have the following workflow:

- Create a new node and upload example.png
- Edit that same node and upload example.png again, which is renamed to example_0.png automatically. But then don't save the node.
- You leave the page and the post is never saved.

So this situation, it shows why FileField doesn't replace the existing file. If the node is never saved, the original file obviously shouldn't be removed. Also if the user uploads a file, they might want to include that file's URL (which is example_0.png) in the body of the post. If we moved the uploaded file after the node was saved, then the URL within the body would break.

jaypabs’s picture

I hope that the way filefield will handle changes will do like this:

If preview then do not upload or overwrite file, since this is not yet the final action. It is just a preview of the node.
If save then overwrite file.

quicksketch’s picture

If save then overwrite file.

This won't work for the reason I stated above:

Also if the user uploads a file, they might want to include that file's URL (which is example_0.png) in the body of the post. If we moved the uploaded file after the node was saved, then the URL within the body would break.

jaypabs’s picture

If the user will use different filename then delete the old one. This will work only on single file upload. If you have setup a multiple upload using file field then do not delete the old file. Instead just append the new one.

quicksketch’s picture

The old file is deleted if it is no longer used. But like I said, we can't rename the new file after it's been uploaded, because the user might use the new file's URL within the post.

jaypabs’s picture

I have seen so many old file which is not deleted after uploading a new file in my server.

Did you update this? On what version? Because I was using filefield since version 2.0 I think, if not version 1.0.

And for the filename again, if the user upload the same filename then filefield should overwrite the old file, and don't rename it something like filename_0.zip. And if you are worrying about referencing a filename in the textfield, then if the user upload with the same filename, then make a reference on that same filename.

quicksketch’s picture

I have seen so many old file which is not deleted after uploading a new file in my server.

They're always deleted eventually. See #419180: Delete Temporary Files Sooner. Note that when updating or editing a node, the files are deleted immediately if they're no longer needed. It's only when you upload a new files but remove them before saving that the files are not deleted immediately.

And for the filename again, if the user upload the same filename then filefield should overwrite the old file, and don't rename it something like filename_0.zip.

No it shouldn't. See all the reasons I've stated above.

quicksketch’s picture

Status: Active » Fixed

#1 explains why we can't do this as the default behavior. A work-around if you absolutely need the same file name: Edit the node, remove the file and save. Edit the node again and upload the new file.

I'm marking this fixed as a support request, since there's nothing else FileField can provide.

jaypabs’s picture

Edit the node, remove the file and save. Edit the node again and upload the new file.

It will, for those who know this process. I have a lot of members, who do not know about this. In users perspective, all they do is change it without doing the process above, as you mentioned.

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

markDrupal’s picture

UPDATE:I moved this to its own module here: http://drupal.org/project/upload_replace

I ran in the same problem as #9. My users cannot be expected to be retrained to use this module by using a work around of delete-save-edit-add

I wrote my own module for fixing this for myself, if anyone else has similar issues, i'd appreciate some feed back or validation that this works for you.
This module notices when a file has been renamed by adding a "_1", "_2" etc and updates the file name by renaming it from <filename>_1.<ext> to <filenamde>.<ext>
It also finds the file that was blocking this action from occurring and swaps names so the newest file will retain its file name and the older file gets the "_1".

Tested with the file field CCK and works with node versioning and reverting old version of a node works as well. When reverting, the reverted node always keeps the correct file name.
Thanks

here are the 2 files: upload_replace.info and upload_replace.module

; $Id:$
name = Upload replace
description = Make the most recent version of a file always retain its original filename, older file with the same name are renamed
core = 6.x
/**
 * @file
 * A module file for providing functionality to replace files on upload
 * Typical Drupal behavior is to rename files on upload to <filename>_0.<ext>
 * This module modifies that behavior.
 *
 * 1.0 - Behavior is as follows: when a user is replacing a file (remove and save).
 *   if the file to be removed is <filename>.<ext> and the file to be saved is
 *   <filename>_<[0-9]+>.<ext> then the file will be removed and the new file will
 *   be renamed to <filename>.<ext>
 */


/**
 * Implementation of hook_file_update()
 */
function upload_replace_file_update(&$new_file) {
  if(!$new_file->fid){
    //Nothing to do if no fileid
    return;
  }
  $desired_destination = preg_replace('/_[0-9]+\.(.*)$/', '.$1', $new_file->filepath);
  $query = "SELECT filepath FROM {files} WHERE fid=%d";
  $db_path = db_result(db_query($query, $new_file->fid));
  if($db_path != $new_file->filepath){
    //this happens when a reversion is being reverted
    $next_good_filepath = file_destination($desired_destination, FILE_EXISTS_RENAME);
    $query = "UPDATE {files} SET filepath='%s' WHERE fid=%d";
    db_query($query, $new_file->fid, $next_good_filepath);
    $new_file->filepath = $desired_destination;
  }
  else {
    //If the filename has be modified by adding a _0 value, or
    //on certain situations the filepath will not match the filepath in the db, such as
    //when reverting a revision.  When reverting a revision change the filename as well
    if(!strpos($new_file->filepath, $new_file->filename)){
      //the filename is not in the filepath, so drupal must have added a "_0" before the extension
      //find the file that is blocking this file from keeping the correct path
      $query = 'SELECT * FROM {files} WHERE filepath="%s"';
      $result = db_query($query, $desired_destination);
      while($file = db_fetch_object($result)){
        $is_blocked = TRUE;
        $blocking_file = $file;
        $tmp_destination = file_directory_temp() . "/$blocking_file->filename";
      }

      $old_destination = $db_path;
      $query = "UPDATE {files} SET filepath='%s' WHERE fid=%d";
      //Swap the files
      if($is_blocked){
        //move the blocking file to a temparary location
        $from = $desired_destination; //because $from gets overwritten by file_move
        if(!file_move($from, $tmp_destination)){
          drupal_set_message('The file %old could not be moved to %new', array('%old' => $desired_destination, '%new' => $tmp_destination), 'error');
          return;
        }
        $tmp_destination = $from; //since the file copy may have placed the file in a modified location update the tmp destination with the actual location
        //move blocking file was successful, update the DB
        db_query($query, $tmp_destination, $blocking_file->fid);
      }


      //move the newfile to the prefered location
      $from = $old_destination;//because $from gets overwritten by file_move
      if(!file_move($from, $desired_destination)){
        drupal_set_message('The file %old could not be moved to %new', array('%old' => $old_destination, '%new' => $desired_destination), 'error');
        return;
      }
      //move newfile was successful, update the DB
      db_query($query, $desired_destination, $new_file->fid);
      $new_file->filepath = $desired_destination;//set the newfile's path to the correct path


      if($is_blocked) {
        //move the older file from temp to the new _0 location
        if(!file_move($tmp_destination, $old_destination)){
          drupal_set_message('The file %old could not be moved to %new', array('%old' => $tmp_destination, '%new' => $old_destination), 'error');
          return;
        }
        //move blocking file was successful, update the DB with the actual location after file copy, so we use tmp_destination as it was updated during the move
        db_query($query, $tmp_destination, $blocking_file->fid);
      }
    }
  }
  //Have to clear the cache because the revision data is cached somewhere
  /*
   * Find the nids where this file is used
  $query = "SELECT DISTINCT nid FROM {files} WHERE fid=%d";
  $result = db_query($query, $new_file->fid);
  while($data = db_fetch_object($result)){
    cache_clear_all("content:$data->nid");
  }
  */
  //This is inefficent, but how can we determine what nodes use this file?
  cache_clear_all('*', 'cache_content', TRUE);
}
markDrupal’s picture

I moved this to its own module here:
http://drupal.org/project/upload_replace

jaypabs’s picture

That was great mark. You have a good idea here. Rename the old file as _1 and retain the new filename.

Berliner-dupe’s picture

Thank you Mark for this helpful module - it works great. I am so happy ;-)

rituraj.gupta’s picture

Issue summary: View changes

hook_file_update is not calling on update the image in Drupal 7. What is the reason ?

ravyg’s picture

I think in code if you are using "FILE_EXISTS_REPLACE" should work not 100% sure.

<?php
 $file_temp = file_save_data($attachment->data, 'public://<my_files_locatioin>/<my-filename.pdf>' , FILE_EXISTS_REPLACE);
  
  $node->field_attachment = array(
    'und' => array(
      0 => array(
        'fid' => $file_temp->fid,
        'filename' => $node->title,
        'filemime' => 'application/pdf',
        'uid' => 1,
        'uri' => $file_temp->uri,
        'status' => 1,
        'display' => 1,
      ),
    ),
  );

  if ($node->nid) {
    node_save($node);
    watchdog('my_module_name', 'my_module_name_attachment_replace() - SUCCESS: %nid Attachment Added/Updated!', array('%nid' => $node->nid), WATCHDOG_NOTICE);
  }
?>

For Reference:
file_save_data($data, $destination = NULL, $replace = FILE_EXISTS_RENAME)

FILE_EXISTS_REPLACE - Replace the existing file. If a managed file with the destination name exists then its database entry will be updated. If no database entry is found then a new one will be created.
FILE_EXISTS_RENAME - Append _{incrementing number} until the filename is unique.
FILE_EXISTS_ERROR - Do nothing and return FALSE.

rituraj.gupta’s picture

@ravg, FILE_EXISTS_REPLACE always work and 100 % sure for this. Please check if parameters are being send correctly and permissions are given correctly to the folders.

johnnny83’s picture

I don't understand why after seven years this is still not in core...

nicholass’s picture

The original solution for this should have been to just ask the user if they would like to overwrite the file when they select the new file to upload or rename it. DONE! Ignore the edge case of someone 'might' not wanting to save the node. Given that didn't happen www.drupal.org/project/upload_replace is the best solution I guess.

johnnny83’s picture

Yeah, I know this module, but unfortunately no D8 port available. Seems like I still have to use FTP :D.

kevinquillen’s picture

FTP doesn't solve this, because it doesn't account for the field data you need to relate fields to entity content and vice versa.

I, for one, would love to have the option when creating a field what the default behavior should be (REPLACE vs RENAME, etc).