CarrierWave:为所有版本化文件创建相同的唯一文件名
在我详细介绍之前,我会明确指出:有没有人想出办法让Carrierwave保存文件,其名称为时间戳或每个文件唯一的任意字符串?
默认情况下,Carrierwave将每个文件及其备用版本保存在自己的目录中(以型号ID号命名)。 我不喜欢这个,因为为了使用一个大的圆形数字而不是一个1000的目录,文件(在我的情况下是图片)中我们得到一个目录,其中有1,000个子目录,每个子目录有一个或两个文件。 呸。
现在,当您覆盖Uploader的store_dir
方法时,如下所示:
def store_dir "uploads/#{model.class.to_s.underscore}/#{mounted_as}" end
你最终得到了我想要的确切行为。 所有文件(图片)都进入一个大的快乐文件夹。 删除对象时不再有子文件夹。
只有一个问题。 文件冲突。 如果你上传了delicious_cake.jpg两次,那么即使它们是美味蛋糕的两张不同的照片,它也会覆盖第一个! 这显然是为什么store_dir
方法在它返回的值的末尾增加了额外的/#{model.id}
。
那么该怎么办? 在阅读了一下后,我发现在生成的上传器文件中有一个明显的解决方案被注释掉了。
# 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
经过一番搜索,我找到了一个做过以下事情的人
def filename @name ||= "#{secure_token}.#{file.extension}" if original_filename end
这让我思考,为什么不这样做呢
def filename @name ||= "#{(Time.now.to_i.to_s + Time.now.usec.to_s).ljust(16, '0')}#{File.extname(original_filename)}" end
事情变得非常糟糕。 这个问题是filename
显然是为filename
的每个版本调用的,所以我们最终得到文件名,如1312335603175322.jpg和thumb_1312335603195323.jpg。 请注意细微差别? 每个文件名都基于为该特定版本调用filename
的时间。 那根本不会做。
我接下来厌倦了使用model.created_at
作为时间戳的基础。 只有一个问题,即第一个版本返回nil,因为它还没有放入数据库。
经过一番思考后,我决定在我的照片控制器中尝试以下操作。
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 ...
这会覆盖原始文件名属性,然后Carrierwave会将其作为时间戳。 它完全符合我的要求。 该文件的原始版本最终得到一个名称,如1312332906940106.jpg,缩略图版本(或任何其他版本)最终得到一个名称,如thumb_1312332906940106.jpg。
但是,这似乎是一个可怕的黑客。 这应该是模型的一部分,或者更好的是安装到模型上的上传器的一部分。
所以,我的问题是,有没有更好的方法来实现这一目标? 我是否错过了Carrierwave至关重要的一些内容? 是否有一个不那么明显但更清洁的方式来解决这个问题? 工作代码很好,但工作代码没有异味更好。
您可以在uploader
文件中执行类似的操作,它也适用于版本化文件(即如果您有一个图像,然后创建同一文件的3个其他缩略图版本,它们将具有相同的名称,只是尺寸信息附加到名称上):
# 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
这将创建一个这样的文件名,例如: 76_a9snx8b81js8kx81kx92.jpg
其中76是模型的id,另一位是随机SHAhex。
另请参阅现在提供的来自carrierwave wiki的解决方案https://github.com/carrierwaveuploader/carrierwave/wiki/How-to:-Use-a-timestamp-in-file-names
您可以在文件名中包含覆盖文件名的时间戳,您可以在Carrierwave文档中阅读:
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
不要忘记在实例变量中记住结果,或者可能会将不同的时间戳写入数据库和文件存储。
- Capistrano的Carrierwave文件
- CarrierWave文件上传无法在rails中运行
- 从WickedPDF获取PDF以通过Carrierwave进行附件
- 未定义的方法裁剪! 在轨道3.1.3上使用Carrierwave和MiniMagick
- 使用CarrierWave上传RESTful文件
- 如何在Rspec中存根载波?
- 使用Amazon S3配置Carrierwave,在Heroku上托管
- Carrierwave_Direct的direct_upload_form_for为FileUploader返回未定义的方法`model_name’:Class
- 使用不同文件类型上载Carrierwave文件