新手问题 carrierwave 的 filename 问题

rdongxie · 2016年06月28日 · 最后由 rdongxie 回复于 2016年06月29日 · 2672 次阅读

在 uploader 中定义 filename 参考: http://huacnlee.com/blog/carrierwave-upload-store-file-name-config/

def filename
    if super.present?
      @name ||= Digest::MD5.hexdigest(current_path)
      "#{@name}.#{file.extension}"
    end
  end

创建一个 model 里面包含一个 avatar 文件属性会创建多个文件

-rw-r--r-- 1 red red 13642 Jun 28 00:46 70cf50e3bc627361d14011ac7e0020f4.png
-rw-r--r-- 1 red red 10933 Jun 28 00:46 big_bb4048b3fc05ca7c31517d264cd97ed5.png
-rw-r--r-- 1 red red  8416 Jun 28 00:46 large_d2f5fda456aabef613ad0ec3ab9c7ae1.png
-rw-r--r-- 1 red red  3213 Jun 28 00:46 normal_35702a3ee884fb3f319bb0e0a3a1980b.png
-rw-r--r-- 1 red red   918 Jun 28 00:46 small_20056cd594137790f249b6b89b8b9c58.png

查看数据库发现 avatar 的值是:70cf50e3bc627361d14011ac7e0020f4.png 显示这张图片的时候使用model.avatar.url(:large),结果 image 的 url 为large_70cf50e3bc627361d14011ac7e0020f4.png,但是并没有生成这个文件 之前生成了large_d2f5fda456aabef613ad0ec3ab9c7ae1.png这个文件。所以显示失败了,

会产生这种情况呢?

@huacnlee 华顺哥,再麻烦你啦。

https://github.com/huacnlee/imax.im/blob/master/app/uploaders/base_uploader.rb#L41

很久没这么用了,之前就是这么做的,一直没问题的

你现在代码是 filename 是一摸一样的么?

#2 楼 @huacnlee 我的代码是

@name ||= Digest::MD5.hexdigest(current_path)
 "#{@name}.#{file.extension}"

和你给的 https://github.com/huacnlee/imax.im/blob/master/app/uploaders/base_uploader.rb#L41 不同之处是: 我的:@name ||= Digest::MD5.hexdigest(current_path) 你的:@name ||= Digest::MD5.hexdigest(File.dirname(current_path))

所有版本的缩略图的 MD5 都是相同的

当初我写那篇文章的时候说过这句,应该是可以的(不过我已经很久没用那个方法了,现在都用云存储内置的图片处理功能)

因为 current_path 是原始文件存到服务器以后的路径,而 @name ||= 这个逻辑会让 Uploader 的实例在存储以及生成缩略图那一段时间都是共用的!

至于你的场景为何有问题,可能是其他部分代码引起的(就不能把你的 Uploader 完整代码贴出来么?!😩 😓

顺哥,我停电了。手机回复下,我再看看,麻烦啦,等会等来电。

@huacnlee 我知道是什么导致的了,我从 AvatarUploader 又继承了一次导致,没有 version 的图片和有 version 的图片调用的 filename 不同,但是还不知道原因是什么,我把代码贴出来。

原因是生成没有 version 的图片是调用 NodeAvatarUploader 的 filename,生成有 version 的图片调用的是 AvatarUploader 的 filename 这个是导致不一样的原因

model 类,

class Node < ApplicationRecord
  include BaseModel
  mount_uploader :avatar, NodeAvatarUploader
end

BaseUploader

require 'carrierwave/processing/mini_magick'
class BaseUploader < CarrierWave::Uploader::Base
  include CarrierWave::MiniMagick

  # Override the directory where uploaded files will be stored.
  # This is a sensible default for uploaders that are meant to be mounted:
  def store_dir
    model.class.to_s.underscore
  end

  # Provide a default URL as a default if there hasn't been a file uploaded:
  def default_url
    "photo/#{version_name}.jpg"
  end

  # Add a white list of extensions which are allowed to be uploaded.
  # For images you might use something like this:
  def extension_white_list
    %w(jpg jpeg gif png)
  end
end

AvatarUploader

class AvatarUploader < BaseUploader
  version :normal do
    process resize_to_fill: [48, 48]
  end

  version :small do
    process resize_to_fill: [16, 16]
  end

  version :large do
    process resize_to_fill: [96, 96]
  end

  version :big do
    process resize_to_fill: [120, 120]
  end
  def filename
    #"#{secure_token}.#{file.extension}" if original_filename.present?
    if super.present?
      "abcdefg123#{file.extension}"   #生成有version的图片调用这个
    end
  end
end

NodeAvatarUploader

# encoding: utf-8

class NodeAvatarUploader < AvatarUploader

  def filename
    if super.present?
       "abaq324231.png"  ##生成没有version的图片调用这个
    end
  end
end

强烈不建议用 MD5 来计算文件名,虽然 current_path 是唯一的,但是并不代表 hash 后得到的签名是唯一的 建议基于 cache_id 来生成, cache_id 实际上就是 current_path 最后一层的路径名 1390890634-26112-1234-2122

我是这样生成 md5 的文件名

def md5
  chunk = model.send(mounted_as)
  @md5 ||= "#{Digest::MD5.hexdigest(chunk.read.to_s)}#{model.id}"
end

def filename
  @name ||= "#{md5}#{File.extname(super)}" if super
end

问题已经变成:生成没有 version 的图片时候是调用 NodeAvatarUploaderD 的 filename,生成有 version 的图片时候是调用 AvatarUploader 的 filename 了

需要 登录 后方可回复, 如果你还没有账号请 注册新账号