【问题标题】:CarrierWave: Create the same, unique filename for all versioned filesCarrierWave:为所有版本化文件创建相同的唯一文件名
【发布时间】:2011-10-18 18:05:30
【问题描述】:

在我详细介绍之前,我要直截了当:有没有人想出一种方法让 Carrierwave 将文件名称保存为时间戳或每个文件唯一的任意字符串?

默认情况下,Carrierwave 将每个文件及其替代版本保存在自己的目录中(以型号 ID 号命名)。我不喜欢这个,因为不是一个有 1,000 个的目录,而是为了使用大整数,文件(在我的例子中是图片)在其中我们得到一个有 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 显然会为文件的每个版本调用,所以我们最终会得到像 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 到达之前覆盖 original_filename 属性,使其成为时间戳。它正是我想要的。文件的原始版本以 1312332906940106.jpg 之类的名称结束,而缩略图版本(或任何其他版本)以 thumb_1312332906940106.jpg 之类的名称结束。

但是,这似乎是一个糟糕的 hack。这应该是模型的一部分,或者更好的是安装在模型上的上传器的一部分。

所以,我的问题是,有没有更好的方法来实现这一点?我是否错过了一些重要的 Carrierwave 使这变得容易?有没有一种不那么明显但更清洁的方式来解决这个问题?工作代码是好的,但不难闻的工作代码更好。

【问题讨论】:

  • 很高兴看到其他人同意将所有图像放在一个快乐的大文件夹中。由于子目录的废话,我拒绝了 Paperclip。您只需要一个唯一的文件名。计算机不需要组织目录,它们非常擅长查找内容。

标签: ruby-on-rails ruby ruby-on-rails-3 filenames carrierwave


【解决方案1】:

您可以在 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,另一位是随机 SHA 十六进制。

【讨论】:

  • 哇。你刚刚完成了我的一周!这完美无缺。我使用我已经使用的格式修改了您的一点点,使其成为时间戳(因为它在美学上让我满意)。但这正是我所需要的,并且以一种非常容易理解和优雅的 Ruby 方式在一个有意义的地方进行。这真的真的真的应该在 Carrierwave 自述文件中。我不可能是唯一一个使用这颗奇妙宝石的人,他们几乎拔掉了头发,试图解决这个看似微不足道的任务。
  • 仅供参考.. 它似乎已经在 CarrierWave Wiki 中 :) github.com/jnicklas/carrierwave/wiki/…
  • 此代码将中断,因为文件名不能使用 model.id,因为它在创建资源之前未设置。
  • 我被文件名中的model.id 烧毁了。不要使用它。见groups.google.com/forum/#!topic/carrierwave/5yiZmAPfMPk我相信载波发生器现在确实包含一个建议,以避免model.idversion.name
【解决方案2】:

还可以查看现在可用的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

不要忘记将结果存储在实例变量中,否则您可能会将不同的时间戳写入数据库和文件存储。

【讨论】:

  • 好多了——这正是我们需要的答案。做得好,感谢您对其进行编辑以包含示例代码和解释。
【解决方案3】:

解决方法和the official documentation中描述的一样

但它总是以nil 的形式返回original_filename。所以只需将其更改为实例变量@original_filename.present?

【讨论】:

  • 你的意思是.present?,不存在a
猜你喜欢
  • 1970-01-01
  • 2018-11-02
  • 1970-01-01
  • 2018-06-16
  • 2017-09-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-23
相关资源
最近更新 更多