22

詳細に入る前に、要点を説明します。Carrierwaveに、名前付きのファイルをタイムスタンプまたは各ファイルに固有の任意の文字列として保存させる方法を見つけた人はいますか?

デフォルトでは、Carrierwaveは各ファイルとその代替バージョンを独自のディレクトリ(モデルID番号にちなんで名付けられた)に保存します。私はこれが好きではありません。なぜなら、1,000個のディレクトリの代わりに、大きなラウンド数のファイル(私の場合は写真)を使用するために、それぞれ1つまたは2つのファイルを持つ1,000個のサブディレクトリを持つ1つのディレクトリを取得するからです。うん。

ここで、アップローダーのstore_dirメソッドをオーバーライドして、次のようにすると、次のようになります。

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

あなたは私が望む正確な振る舞いになってしまいます。すべてのファイル(写真)は、1つの大きな幸せなフォルダーに入れられます。オブジェクトが削除されたときに固執するサブフォルダーはもうありません。

問題は1つだけです。ファイルの衝突。おいしいケーキの2つの異なる写真であっても、delicious_cake.jpgを2回アップロードすると、2番目の写真が最初の写真を上書きします。これが、メソッドが返す値の最後に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が返されるという問題が1つだけあります。

さらに考えた後、写真コントローラーで次のことを試してみることにしました。

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のような名前になります。

しかし、これはひどいハックのようです。これはモデルの一部である必要がありますが、モデルにマウントされているアップローダーの一部である必要があります。

だから、私の質問は、これを達成するためのより良い方法はありますか?これを簡単にするCarrierwaveの重要な何かを見逃しましたか?これについてはそれほど明白ではありませんが、よりクリーンな方法はありますか?動作するコードは良いですが、悪臭のない動作するコードの方が優れています。

4

3 に答える 3

21

ファイルでこのようなことを行うことができます。これは、uploaderバージョン管理されたファイルでも機能します(つまり、1つの画像があり、同じファイルの他の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であり、他のビットはランダムなSHA16進数です。

于 2011-08-03T06:05:04.427 に答える
3

現在入手可能なcarrierwavewikiのソリューションも確認してください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

結果をインスタンス変数に記憶することを忘れないでください。そうしないと、データベースとファイルストアに異なるタイムスタンプが書き込まれる可能性があります。

于 2017-07-25T07:13:17.597 に答える
0

解決策は、公式ドキュメントに記載されているものと同じです

ただし、常にとして返さoriginal_filenamenilます。したがって、インスタンス変数に変更するだけです。@original_filename.present?

于 2020-02-18T09:16:15.847 に答える