使用载波上传器进行图像,尝试使用 md5 校验和作为文件名来提供上传图像的唯一性,
看起来我正在做一些错误的事情
模型定义如下:
class Image < ActiveRecord::Base
attr_accessible :description, :img
mount_uploader :img, ImageUploader
我的上传器代码如下:
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
首先,我怀疑这种方法会造成校验和的计算每次查询图片条目进行
第二次显示,保存图片条目后,每隔一个 img.original_filename
img.filename
img.path
img.current_path
似乎未定义,并出现以下错误:
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'
感谢任何类型的帮助或提示
UPD:
以这种方式更改了上传者:
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
似乎指的是表单提交的临时文件的完整路径,因此对于扩展名提取和摘要计算有效
img_identifier
代表持久化结果文件名,因此对于我们的 store_dir
的前缀提取有效
仍然不确定这种方法是否会引起任何警告,
也仍然不相信应该执行文件唯一性验证的方式
UPD:
我在我的模型中添加了这个before_validation
回调类:
validates_uniqueness_of :checksum
before_validation :assign_checksum
def assign_checksum
self.checksum = img.md5 if img.present? and img_changed?
end
其中 checksum
是我模型的数据库表中的一个单独的字符串字段
它是相当多余,因为它一般会重复img
字段,但我仍然无法找出验证img
本身唯一性的方法。
UPD:
通过这种方式摆脱了数据库冗余。在我的模型中:
validate :img_uniqueness
def img_uniqueness
errors.add :img, "Image already exists in database" if Image.where(:img => self.img.filename).first
end
现在不需要 checksum
字段
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
发布评论
评论(4)
这可以帮助:
如何:使用文件的 MD5 作为文件名
https://github.com /rierwaveuploader/carrierwave/wiki/How-to:-Use-file%60s-MD5-as-filename
也许还有
How-to:使用文件的摘要(例如 MD5、SHA-1)作为文件路径
https://github.com/rierwaveuploader/rierwave/wiki/How-to:-Use-file's-digest-(eg-MD5,-SHA-1)-as-file-path
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
将 md5hash 字段添加到您的模型中,然后将以下代码添加到您的模型中:
这应该可以解决问题。
Add a md5hash field to your model, then add the following code to your Model:
That should do the trick.
1.当您定义
store_dir
.. filename 似乎是 NIL!!这似乎是立即错误 - 尝试使用
puts
语句打印出来文件名设置为什么..如果文件名是 NIL,您将看到所看到的错误:
注意:
您正在覆盖两者文件名和 store_dir ...并在其中使用文件名store_dir 的定义...
这里可能存在“先有鸡还是先有蛋”的问题。更好地检查
store_dir 应该只是一个目录,例如
/somewhere/on/your/disk/images
filename 应该只是一个不带路径的文件名,例如
24371592d9ea16625854ed68ac4b5846
,或者24371592d9ea16625854ed68ac4b5846.jpg
例如检查这两个在
store.rb
的代码中如何使用(在下面的末尾)问题:
使用
filename[0,2] - 您正在使用带有 MD5-sum 2 字母前缀的目录来存储图像?
2.旁注:什么是
current_path
? 似乎用词不当。应该是路径+文件名,而不仅仅是路径3。检查
store.rb
中的第二个代码片段(如下)似乎您将
store_dir
设置为相对目录 - 这是非常脆弱且容易出错的..将其设置为绝对路径(以“/”开头)4可能是一个更好的主意。尝试将 filename 和 store_dir 设置为常量以进行调试
就像健全性检查一样,当您执行此操作时它是否有效:
在将 filename 设置为 MD5 sum 之前应该有效。
来自源代码: (0.5.7)
lib/rierwave/storage/file.rb
lib/rierwave/uploader/store.rb
您可以覆盖 CarrierWave::Uploader::Store#filename 以指向您选择的文件名(请参阅源代码):
在 CarrierWave::Uploader::Base 中覆盖它应该可以工作,因为 'Store' 包含在 'Base' 中;这会覆盖默认文件名。
From
store.rb
您还可以检查缓存的文件名,计算 MD5 和(或更好的 SHA1 和),然后使用结果来命名文件。
来自
store.rb
文件相等:
1. When you define
store_dir
.. filename seems to be NIL!!That seems to be the immediate error -- try a
puts
statement to print out what filename is set to..If the filename is NIL, you'll see the error you're seeing:
NOTE:
you're overriding both filename and store_dir ... and using filename inside the definition of store_dir...
There could be a "chicken and the egg" type of problem here.. better check on that
store_dir should just be a directory, e.g.
/somewhere/on/your/disk/images
filename should just be a filename without path, e.g.
24371592d9ea16625854ed68ac4b5846
, or24371592d9ea16625854ed68ac4b5846.jpg
e.g. check how those two are used in the code in
store.rb
(at end below)Question:
using
filename[0,2]
- you're using directories with 2-letter prefix of the MD5-sum to store the images?2. Side-note: What is
current_path
?? Seems a misnomer. Should be a path+filename, not just a path3. Check the second code snippet (below) from
store.rb
Seems like you set
store_dir
to a relative directory -- that's very fragile and error prone.. it might be a better idea to set it to an absolute path (starting with '/')4. Try setting filename and store_dir to constants for debugging
Just as a sanity check, does it work when you do this:
that should work before setting filename to MD5 sum..
From the source code: (0.5.7)
lib/carrierwave/storage/file.rb
lib/carrierwave/uploader/store.rb
You can override CarrierWave::Uploader::Store#filename to point to a filename of your choice (see source code):
Overriding it in CarrierWave::Uploader::Base should work, as 'Store' is included in 'Base' ; this overrides the default filename.
From
store.rb
You could also check for the cached filename, compute the MD5-sum (or better SHA1 sum) on it, and then use the result to name the file.
From
store.rb
File Equality:
我会考虑文件名唯一性的wiki方式,
https://github.com/jnicklas/rierwave/wiki/How-to%3A-Create-random-and-unique-filenames-for-all-versioned-files
I would consider the wiki way for file name uniquess,
https://github.com/jnicklas/carrierwave/wiki/How-to%3A-Create-random-and-unique-filenames-for-all-versioned-files