CarrierWave:为所有版本文件创建相同的唯一文件名 [英] CarrierWave: Create the same, unique filename for all versioned files

查看:47
本文介绍了CarrierWave:为所有版本文件创建相同的唯一文件名的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在开始详细介绍之前,我先说清楚:有没有人想出一种方法来让Carrierwave将带有名称的文件保存为时间戳或每个文件唯一的任意字符串?

Before I go into detail I'll get right to the point: has anyone figured out a way to get Carrierwave to save files with their names as a timestamp or any arbitrary string that is unique to each file?

默认情况下,Carrierwave将每个文件及其备用版本保存在其自己的目录中(以型号ID号命名)。我不喜欢这样做,因为不是使用一个包含1,000个目录的目录,而是使用一个较大的整数,而是使用一个包含1000个子目录的目录(每个文件包含一个或两个文件),以便使用较大的整数。 uck。

By default Carrierwave saves each file and its alternate versions in its own directory (named after the model ID number). I'm not a fan of this because instead of one directory with 1,000, for the sake of using a large round number, files (in my case pictures) in it we get one directory with 1,000 subdirectories each with one or two files. Yuck.

现在,当您覆盖上载程序的 store_dir 方法时,它类似于以下内容:

Now, when you override your Uploader's store_dir method to be something like the following:

def store_dir
  "uploads/#{model.class.to_s.underscore}/#{mounted_as}"
end

您最终会得到我想要的确切行为。所有文件(图片)进入一个大的快乐文件夹。

you end up with the exact behavior that I want. All the files (pictures) go into one big happy folder. No more subfolders that stick around when the object gets deleted.

只有一个问题。文件冲突。如果您上传两次Delicious_cake.jpg,则第二张照片将覆盖第一个,即使它们是两张不同的美味蛋糕图片!这就是为什么 store_dir 方法在值的末尾附加了额外的 /#{model.id} 的原因返回。

There's only one problem. File collisions. If you upload delicious_cake.jpg twice the second one will overwrite the first even if they are two different pictures of delicious cake! That's clearly why the store_dir method has the extra /#{model.id} tacked on the end of the value it returns.

那该怎么办?经过一番阅读后,我发现在生成的上传器文件中有一个明显的解决方案被注释掉了。

So, what to do? After reading around a bit I discovered that in the generated uploader file there is an apparent solution commented out.

# 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

经过一番搜索,我发现有人做了以下

After a little bit of searching I found someone who had done the following

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

这让我开始思考,为什么不这样做

This got me thinking, why not just do this

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 的时间。

That's when things got horribly broken. The problem with this is that filename apparently gets called for each version of the file so we end up with file names like 1312335603175322.jpg and thumb_1312335603195323.jpg. Notice the slight difference? Each file name is based on the time when filename was called for that particular version. That won't do at all.

我接下来就用 model.created_at 作为时间戳了。只有一个问题,因为第一个版本尚未放入数据库,所以它返回nil。

I next tired using model.created_at for the basis of the timestamp. Only one problem, that returns nil for the first version since it hasn't been put in the database yet.

经过进一步思考,我决定尝试以下方法图片控制器。

After some further thinking I decided to try the following in my pictures controller.

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结尾。

This overrides the original_filename property before Carrierwave even gets to it making it be a timestamp. It does exactly what I want. The original version of the file ends up with a name like 1312332906940106.jpg and the thumbnail version (or any other version) ends up with a name like thumb_1312332906940106.jpg.

但是,这似乎是一个可怕的骇客。这应该是模型的一部分,或者最好是一部分安装在模型上的上载器。

But, this seems like an awful hack. This should be part of the model, or better yet part of the uploader mounted onto the model.

所以,我的问题是,有没有更好的方法来实现这一目标?我是否错过了使Carrierwave变得如此简单的关键因素?有没有那么明显但更干净的方法来解决这个问题?工作代码不错,但不会臭的工作代码更好。

So, my question is, is there a better way to achieve this? Did I miss something crucial with Carrierwave that makes this easy? Is there a not so obvious but cleaner way of going about this? Working code is good, but working code that doesn't smell bad is better.

推荐答案

您可以在自己的代码中执行类似的操作 uploader 文件,它也适用于版本控制文件(即,如果您有一个图像,然后创建同一文件的其他3个缩略图版本,则它们都将具有相同的名称,只需在名称上附加大小信息):

You can do something like this in your uploader file, and it will also work for versioned files (i.e. if you have one image and then create 3 other thumbnail versions of the same file, they will all have the same name, just with size info appended onto the name):

  # 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 h例如。

This will create a filename like this for example: 76_a9snx8b81js8kx81kx92.jpg where 76 is the model's id and the other bit is a random SHA hex.

这篇关于CarrierWave:为所有版本文件创建相同的唯一文件名的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆