Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to keep the same filename in carrierwave when updating a file (in rails)

When an image is updated using carrierwave I have it setup to use the model id and original file name

def filename
 "#{model.id}-#{original_filename}" if original_filename.present?
end

Its only like this because its the default way I found when I was setting up carrierwave. Recently I realised if you update an image the link to the image is broken because the filename is updated to use the newer files name. So if a page is linked to the old image it isn't updated with the newer image but instead becomes a broken image link.

I want it to keep the old file name when the image is being updated but cant find how to do this in the documentation and from googling around

Edit

I diddnt add a lot of the 'use case' because I diddnt think the problem was this complicated.

Basically on the site there are items that have images assigned to them. I did see the comment on not using only model.id and @struthersneil cleared up why that comment is there. Considering the images records for an items are created when the item is created they will never be new when the image is being added (because its just updating a record that already exists). Because of this I'd say my best bet is to use just model.id as the filename.

My other option is to keep the old image so the links aren't broken. The main problem for using this is that users create posts on the site and link to these images in the posts (the posts are regularly updated so I cant to a once off cache on the post and images in the post). The images become updated from new information on the items they are associated to. Once this happens the image linked in the post will show outdated information and they will have to manually update to the new image each time. So having the image link stay the same with the image being updated is the best option in this case particular case

like image 611
Rob Avatar asked Aug 15 '16 09:08

Rob


1 Answers

original_filename in this context is the name of the file that was just received by the uploader. It's the file's original name.

By doing this:

 "#{model.id}-#{original_filename}" if original_filename.present?

You're telling CarrierWave to use the new name of the file every time. It sounds like you want a given model to use the same filename every time. So the obvious option is simply to throw away the original_filename altogether and use the model id by itself.

However, the comment above the filename method in the auto-generated uploader code says this:

  # Avoid using model.id or version_name here, see uploader/store.rb for details.

Which leads us to this...

  # Be careful using record ids as filenames. If the filename is stored in the database
  # the record id will be nil when the filename is set. Don't use record ids unless you
  # understand this limitation.

That is kind of a hitch. Unless you can guarantee that your model always has an id (which won't be true if it's new) then you shouldn't use model.id to name your file.

I would suggest you generate a unique file_identifier string once for each model on initialize (as long as the file_identifier has not already been set), and just use this unique string as your filename, e.g.

class Something < ActiveRecord::Base
  mount_uploader :image, SomeImageUploader

  after_initialize do
    unless self.file_identifier
      self.file_identifier = SecureRandom.hex(32)
    end
  end
end

...and in your uploader class...

def filename
  model.file_identifier if original_filename
end

Now your uploaded image will always overwrite the same file. But notice: you have just lost your image extension. That seems to be fine in my modern version of Firefox (it can guess by the looking at the image header) but it's probably not the most widely compatible solution. You could take the extension of the original_filename and add it on there, but now you're back to your original problem if someone uploads a png and then uploads a jpg (the result would be two different files).

You could perform a conversion to a fixed format of your choice on upload, then the filename + extension would always be the same.

Honestly I think you'd have an easier time if you accept that files get a new name after every upload, and simply leave the old files hanging around for a set period of time to continue supporting users who have the old link. (How old are these links going to get anyway? If it's sitting in a cache, then why cache only the generated HTML without caching the image?) This is especially true if you ever plan to use a CDN service between your application and your users. The usual pattern is to version your files with a new unique filename every time, which the CDN will cache once and forever.

like image 97
struthersneil Avatar answered Sep 30 '22 18:09

struthersneil