Carrierwave 如何配置合理的上传文件名

一直在寻找一个好的 Carrierwave 上传文件命名结构(GridFS),今天终于找到了,这个方式比较靠谱。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# coding: utf-8
require "digest/md5"
require 'carrierwave/processing/mini_magick'
class BaseUploader < CarrierWave::Uploader::Base
  include CarrierWave::MiniMagick
  storage :grid_fs

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

  # 调整临时文件的存放路径,默认是再 public 下面
  def cache_dir
    "#{Rails.root}/tmp/uploads"
  end

  # TODO: 此处要想办法,开启了 open-uri 下载的因为文件名的问题无法通过验证
  # Allow image file extensions
  def extension_white_list
    %w(jpg jpeg gif png)
  end

  # Override the filename of the uploaded files:
  def filename
    if original_filename
      # current_path 是 Carrierwave 上传过程临时创建的一个文件,有时间标记
      # 例如:/Users/jason/work/ruby-china/public/uploads/tmp/20131105-1057-46664-5614/_____2013-11-05___10.37.50.png
      @name ||= Digest::MD5.hexdigest(current_path)
      "#{@name}.#{file.extension}"
    end
  end
end

这么做的原因:

  • carrent_path 的算法 Time.now.utc.to_i.to_s + '-' + Process.pid.to_s + '-' + ("%04d" % rand(9999)) + 原始文件名,所以理论上重复的概率非常低,外加上一般上传并不会有那种瞬间并发出现的场景,来源
  • 所有版本的缩略图的 MD5 都是相同的
  • 简洁
  • 注意!此处在某些高并发上传的情况下,可能有微妙的概率会导致 @name 重复,比如在批量导入的时候

以前曾经试过这些方式(都不靠谱):

  • 原始文件名 + model.class_name + model.id
  • 用 model.updated_at + model.class_name
  • 用文件 MD5,原始文件无法取到,结果每个缩略图的文件名都不同
  • 用 model.class_name + model.id

如果你用过 Carrierwave 上面我所的应该能理解,具体我就不解释了。