Generating a random filename for the original, but not for every version.

1,979 views
Skip to first unread message

karb

unread,
Feb 25, 2011, 11:19:00 AM2/25/11
to carrierwave
I am using this example from the wiki:

def filename
@name ||= ActiveSupport::SecureRandom.hex
"#{@name}#{File.extname(original_filename).downcase}" if
original_filename
end

To generate a random filename, however it generates a new random hex
for every version. How would I go about creating one random_filename
in the uploader class and using that one for every version (with the
version prefix of course in the filename)?

Thanks in advance!

Trevor Turk

unread,
Feb 25, 2011, 5:31:03 PM2/25/11
to carri...@googlegroups.com
On Friday, February 25, 2011 4:19:00 PM UTC, karb wrote:
How would I go about creating one random_filename
in the uploader class and using that one for every version (with the
version prefix of course in the filename)?

I thought about this a bit, and I wonder if a workaround might be to make a hash of the filename that's not exactly random. Something like:

  def filename 
    @name ||= Digest::MD5.hexdigest "#{original_filename}#{model.class}#{mounted_as}#{model.id}"
  end 

...of course that code wouldn't work, but maybe you see what I'm getting at. 

karb

unread,
Feb 26, 2011, 10:15:53 AM2/26/11
to carrierwave
On Feb 25, 11:31 pm, Trevor Turk <trevort...@gmail.com> wrote:

> make a hash of the filename that's not exactly random. Something like:
>
>   def filename
>     @name ||= Digest::MD5.hexdigest
> "#{original_filename}#{model.class}#{mounted_as}#{model.id}"
>   end

I see what you're getting at but that doesn't solve the problem why
I'm using a random filename in the first place.
Consider this:
- User uploads picture.jpg
- Store it as md5 hash
- User uploads updated picture.jpg (same name)
- Gives the same md5 hash, hence, same filename for another file.

Micke Lisinge

unread,
Feb 26, 2011, 3:27:31 PM2/26/11
to carri...@googlegroups.com

You can use the updated_at from the model. But make to a timestamp by using updated_at.to_i

Thats what i use for my photos

> --
> You received this message because you are subscribed to the Google Groups "carrierwave" group.
> To post to this group, send email to carri...@googlegroups.com.
> To unsubscribe from this group, send email to carrierwave...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/carrierwave?hl=en.
>

karb

unread,
Feb 28, 2011, 11:50:45 AM2/28/11
to carrierwave
On Feb 26, 9:27 pm, Micke Lisinge <mi...@lisinge.com> wrote:
> You can use the updated_at from the model. But make to a timestamp by using
> updated_at.to_i

That works like a charm! Thanks.
Side-note: This could be problematic when used in a multi-user
(thousands) environment, but when stored in folders on a per-user
basis this isn't a problem.

karb

unread,
Mar 1, 2011, 7:13:43 AM3/1/11
to carrierwave
Correction, it doesn't work upon creation of the model, because there
isn't an updated_at attribute available then.
The problem is, that when you ask for the url after upload, updated_at
-is- available and it returns an url that won't be valid.

How do you handle this on creation of new records?

karb

unread,
Mar 1, 2011, 7:28:35 AM3/1/11
to carrierwave
It has to do with something user 'mg' discovered in this thread:
http://groups.google.com/group/carrierwave/browse_thread/thread/7216c2aca0266794/47b5dab5d3238a0d

"AFTER I rearranged the datamapper hook to create random string on
create instead of before save. Before saving the randomstring wasn't
accessible to the model. "

Obviously, that's not at all what we have to go through to accomplish
such a simple task as having a consistent unique filename across
versions?
Is there a reason for the filename function getting called 2 times for
every version? first when it didn't create the record yet, and
afterwards when it did and uploads it off to s3.
So you wind up with a 0.jpg in the database and a correct string on
s3, but you can't refer to it.

Trevor Turk

unread,
Mar 1, 2011, 7:49:53 AM3/1/11
to carri...@googlegroups.com
On Tuesday, March 1, 2011 12:28:35 PM UTC, karb wrote:
Obviously, that's not at all what we have to go through to accomplish
such a simple task as having a consistent unique filename across
versions?

Perhaps you could introduce a new attribute called "unique_hash" or something, calculate that once, and then use that in your filename definition. 
 
Is there a reason for the filename function getting called 2 times for
every version? first when it didn't create the record yet, and
afterwards when it did and uploads it off to s3.
So you wind up with a 0.jpg in the database and a correct string on
s3, but you can't refer to it.

I'm not sure - of course, please feel free to investigate more about this. We can discuss more in a pull request if you like. 

karb

unread,
Mar 1, 2011, 7:53:25 AM3/1/11
to carrierwave
On Mar 1, 1:49 pm, Trevor Turk <trevort...@gmail.com> wrote:
> please feel free to investigate more about this.
> We can discuss more in a pull request if you like.

Will definitely think about this some more and tinker with the code,
as soon as I have something ready will put it in a pull request.
More thoughts on this in this thread couldn't hurt tho =)

Skye Shaw

unread,
Apr 14, 2011, 5:29:59 PM4/14/11
to carrierwave


Trevor Turk wrote:
> On Tuesday, March 1, 2011 12:28:35 PM UTC, karb wrote:
> >
> > Obviously, that's not at all what we have to go through to accomplish
> > such a simple task as having a consistent unique filename across
> > versions?
> >
>
> Perhaps you could introduce a new attribute called "unique_hash" or
> something,

Depending on how the temp file is stored there's already a unique
attribute: the temp path. How about:

class AvatarUploader < CarrierWave::Uploader::Base
def filename
if original_filename
@name ||= Digest::MD5.hexdigest(File.dirname(current_path))
"#{@name}#{File.extname(original_filename)}"
end
end
end

-Skye

Corin Langosch

unread,
Apr 29, 2011, 6:10:41 AM4/29/11
to carri...@googlegroups.com
How about this solution? Works great for me :)

# encoding: utf-8
class AvatarUploader < CarrierWave::Uploader::Base

  ...
  
  def filename
    @name ||= "#{model.id}_#{secure_token}.jpg"
  end

  private
  
  def secure_token
    ivar = "@#{mounted_as}_secure_token"
    token = model.instance_variable_get(ivar)
    token ||= model.instance_variable_set(ivar, ActiveSupport::SecureRandom.hex(4))  
  end
end

Message has been deleted

iwasrobbed

unread,
May 24, 2011, 1:12:05 AM5/24/11
to carri...@googlegroups.com
Thanks Corin, that worked for me!  I really hope they rethink this feature in CarrierWave in the future.  This was more of a headache than it should have been.

Andrew Crookston

unread,
Jun 3, 2011, 6:21:53 AM6/3/11
to carri...@googlegroups.com
I tried your solution and came across a couple of problems.
1. The model.id value is not always available, and should not be used in file names, see more in this thread: http://groups.google.com/group/carrierwave/browse_thread/thread/e728999803df30f9/0d49395188905cd0?lnk=gst&q=model.id#0d49395188905cd0 
2. The random hex string was being regenerated for every view of this file (image in my case).

I solved both problems by changing the filename method to:

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

Trevor Turk

unread,
Jun 5, 2011, 12:42:54 PM6/5/11
to carri...@googlegroups.com
Please do consider updating the wiki with this kind of thing. Things get lost in the Google Group, and this kind of thing gets brought up a lot!

karb

unread,
Jun 5, 2011, 1:04:58 PM6/5/11
to carri...@googlegroups.com
Reply all
Reply to author
Forward
0 new messages