Using carrierwave uploader for images, trying to provide uniqueness of uploaded images using md5 checksum as filename
looks like I'm doing something wrong
model is defined like:
class Image < ActiveRecord::Base
attr_accessible :description, :img
mount_uploader :img, ImageUploader
My uploader code is as following:
class ImageUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
storage :file
def store_dir
"images/#{filename[0,2]}"
end
def md5
@md5 ||= ::Digest::MD5.file(current_path).hexdigest
end
def filename
@name ||= "#{md5}#{::File.extname(current_path)}" if super
end
first of all, I suspect this approach inflicts calculation of checksum each time image entry is queried to display
secondly, after image entry is saved, every other of img.original_filename
img.filename
img.path
img.current_path
seem to be undefined with following error:
You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.[]
app/uploaders/image_uploader.rb:17:in `store_dir'
carrierwave (0.5.7) lib/carrierwave/uploader/store.rb:43:in `store_path'
carrierwave (0.5.7) lib/carrierwave/storage/file.rb:41:in `retrieve!'
carrierwave (0.5.7) lib/carrierwave/uploader/store.rb:95:in `block in retrieve_from_store!'
carrierwave (0.5.7) lib/carrierwave/uploader/callbacks.rb:17:in `with_callbacks'
carrierwave (0.5.7) lib/carrierwave/uploader/store.rb:94:in `retrieve_from_store!'
carrierwave (0.5.7) lib/carrierwave/mount.rb:311:in `uploader'
any kind of help or tip is appreciated
UPD:
changed uploader this way:
def store_dir
"images/#{model.img_identifier[0,2]}"
end
def filename
@name ||= "#{md5}#{::File.extname(current_path)}"
end
protected
def md5
var = :"@#{mounted_as}_md5"
model.instance_variable_get(var) or model.instance_variable_set(var, ::Digest::MD5.file(current_path).hexdigest)
end
current_path
seems to refer to full path of form-submitted tempfile, thus being valid for extension extraction and digest calculation
img_identifier
stands for persisting resulting filename and thus goes valid for prefix extraction for our store_dir
still not sure if any caveat is induced with this approach
also still not convinced about the way file uniqueness validation should be performed
UPD:
I've added this before_validation
callback in my model class:
validates_uniqueness_of :checksum
before_validation :assign_checksum
def assign_checksum
self.checksum = img.md5 if img.present? and img_changed?
end
where checksum
is a separate string field in my model's db table
it is quite redundant as it duplicates the img
field in general, but I still can't figure out the way to validate uniqueness of img
itself.
UPD:
Moved away from db redundancy this way. In my model:
validate :img_uniqueness
def img_uniqueness
errors.add :img, "Image already exists in database" if Image.where(:img => self.img.filename).first
end
now there's no need in checksum
field
This could help: How to: Use file`s MD5 as filename https://github.com/carrierwaveuploader/carrierwave/wiki/How-to:-Use-file%60s-MD5-as-filename
And maybe
How-to: Use file's digest (e.g. MD5, SHA-1) as file path https://github.com/carrierwaveuploader/carrierwave/wiki/How-to:-Use-file's-digest-(e.g.-MD5,-SHA-1)-as-file-path
Add a md5hash field to your model, then add the following code to your Model:
before_validation :compute_hash
validates_uniqueness_of :md5hash, :on => :create
def compute_hash
self.md5hash = Digest::MD5.hexdigest(self.file.read)
end
That should do the trick.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With