Create an account

Very important

  • To access the important data of the forums, you must be active in each forum and especially in the leaks and database leaks section, send data and after sending the data and activity, data and important content will be opened and visible for you.
  • You will only see chat messages from people who are at or below your level.
  • More than 500,000 database leaks and millions of account leaks are waiting for you, so access and view with more activity.
  • Many important data are inactive and inaccessible for you, so open them with activity. (This will be done automatically)


Thread Rating:
  • 371 Vote(s) - 3.5 Average
  • 1
  • 2
  • 3
  • 4
  • 5
CarrierWave: Create the same, unique filename for all versioned files

#1
Before I go into detail I'll get right to the point: has anyone figured out a way to get Carrierwave to save files with their names as a timestamp or any arbitrary string that is unique to each file?

By default Carrierwave saves each file and its alternate versions in its own directory (named after the model ID number). I'm not a fan of this because instead of one directory with 1,000, for the sake of using a large round number, files (in my case pictures) in it we get one directory with 1,000 subdirectories each with one or two files. Yuck.

Now, when you override your Uploader's `store_dir` method to be something like the following:

def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}"
end

you end up with the exact behavior that I want. All the files (pictures) go into one big happy folder. No more subfolders that stick around when the object gets deleted.

There's only one problem. File collisions. If you upload delicious_cake.jpg twice the second one will overwrite the first even if they are two different pictures of delicious cake! That's clearly why the `store_dir` method has the extra `/#{model.id}` tacked on the end of the value it returns.

So, what to do? After reading around a bit I discovered that in the generated uploader file there is an apparent solution commented out.

# Override the filename of the uploaded files:
# Avoid using model.id or version_name here, see uploader/store.rb for details.
# def filename
# "something.jpg" if original_filename
# end

After a little bit of searching I found someone who had done the following

def filename
@name ||= "#{secure_token}.#{file.extension}" if original_filename
end

This got me thinking, why not just do this

def filename
@name ||= "#{(Time.now.to_i.to_s + Time.now.usec.to_s).ljust(16, '0')}#{File.extname(original_filename)}"
end

That's when things got horribly broken. The problem with this is that `filename` apparently gets called for each version of the file so we end up with file names like 1312335603175322.jpg and thumb_1312335603195323.jpg. Notice the slight difference? Each file name is based on the time when `filename` was called for that particular version. That won't do at all.

I next tired using `model.created_at` for the basis of the timestamp. Only one problem, that returns nil for the first version since it hasn't been put in the database yet.

After some further thinking I decided to try the following in my pictures controller.

def create
if params[:picture] and params[:picture][:image]
params[:picture][:image].original_filename = "#{(Time.now.to_i.to_s + Time.now.usec.to_s).ljust(16, '0')}#{File.extname(params[:picture][:image].original_filename)}"
end
...

This overrides the original_filename property before Carrierwave even gets to it making it be a timestamp. It does exactly what I want. The original version of the file ends up with a name like 1312332906940106.jpg and the thumbnail version (or any other version) ends up with a name like thumb_1312332906940106.jpg.

But, this seems like an awful hack. This should be part of the model, or better yet part of the uploader mounted onto the model.

So, my question is, is there a better way to achieve this? Did I miss something crucial with Carrierwave that makes this easy? Is there a not so obvious but cleaner way of going about this? Working code is good, but working code that doesn't smell bad is better.
Reply

#2
The solution is the same as described in [the official documentation](

[To see links please register here]

)

But it always returns ```original_filename``` as ```nil```. So just change it to instance variable as ```@original_filename.present?```
Reply

#3
Check also the solution from carrierwave wiki available now

[To see links please register here]


You can include a timestamp in filenames overriding the filename as you can read in Carrierwave docs:

class PhotoUploader < CarrierWave::Uploader::Base
def filename
@name ||= "#{timestamp}-#{super}" if original_filename.present? and
super.present?
end

def timestamp
var = :"@#{mounted_as}_timestamp"
model.instance_variable_get(var) or model.instance_variable_set(var, Time.now.to_i)
end
end

Don't forget to memorize the result in an instance variable or you might get different timestamps written to the database and the file store.
Reply

#4
You can do something like this in your `uploader` file, and it will also work for versioned files (i.e. if you have one image and then create 3 other thumbnail versions of the same file, they will all have the same name, just with size info appended onto the name):

# Set the filename for versioned files
def filename
random_token = Digest::SHA2.hexdigest("#{Time.now.utc}--#{model.id.to_s}").first(20)
ivar = "@#{mounted_as}_secure_token"
token = model.instance_variable_get(ivar)
token ||= model.instance_variable_set(ivar, random_token)
"#{model.id}_#{token}.jpg" if original_filename
end

This will create a filename like this for example: `76_a9snx8b81js8kx81kx92.jpg` where 76 is the model's id and the other bit is a random SHA hex.
Reply



Forum Jump:


Users browsing this thread:
1 Guest(s)

©0Day  2016 - 2023 | All Rights Reserved.  Made with    for the community. Connected through