4

Rails 4 プロジェクトで、開発とテスト用のファイル ストレージと本番用のフォグ ストレージ (Amazon S3 に保存するため) を使用して、carrierwave を使用しています。

次のようなパスでファイルを保存したいと思います。

/model_class_name/part_of_hash/another_part_of_hash/hash-model_id.file_extension

(例: /images/12/34/1234567-89.png1234567 はファイル コンテンツの SHA1 ハッシュであり、89 はデータベース内の関連付けられたイメージ モデルの ID です)。

これまでに試したことは次のとおりです。

class MyUploader < CarrierWave::Uploader::Base

  def store_dir
    "#{model.class.name.underscore}/#{sha1_for(file)[0..1]}/#{sha1_for(file)[2..3]}"
  end

  def filename
    "#{sha1_for(file)}-#{model.id}.#{file.extension}" if original_file
  end

  private

    def sha1_for file
      Digest::SHA1.hexdigest file.read
    end

end

これは次の理由で機能しません。

  • model.idfilenameが呼び出されたときは利用できません
  • filestore_dirが呼び出されたときに常に利用できるとは限りません

だから、私の質問に来て:

  • 内でモデル ID/属性を使用することは可能filenameですか? このリンクは、それを行うべきではないと言っています。それを回避する方法はありますか?
  • 内でファイルのコンテンツ/属性を使用することは可能store_dirですか? これに関するドキュメントは見つかりませんでしたが、これまでの私の経験では「いいえ」と言われています(上記を参照)。
  • 最初に概説したものにできるだけ近いものを得るために、ファイル/ディレクトリの命名をどのように実装しますか?
4

3 に答える 3

5
  • ファイル名はデータベースに保存されていますが、ID はまだ利用できないため、作成時にファイル名に ID を含めることはできない場合があります。(かなり極端な) 回避策は、作成時に一時的な値を使用してからafter_commit on: :create、ファイルを移動してデータベース内の名前を変更することです。これを で最適化することは可能かもしれませんがafter_create、それはあなたに任せます。(これは、carrierwave が実際にファイルをアップロードする場所です。)

  • は —<code>urlの計算に使用されるため、ファイル属性を に直接store_dir含めることはできません。sha1 を知る必要があり、それにはファイルへのアクセス権が必要であり、それには url を知る必要があるなどです。回避策は非常に明白です。関心のある属性 (この場合は sha1) をモデルのデータベース レコードにキャッシュし、.store_dirurlstore_dir

  • id-in-filename アプローチのより単純な変形は、uuid などの他の値を使用し、その値をデータベースに保存することです。ここにいくつかの注意事項があります

于 2013-08-27T14:17:43.860 に答える
2

最近、レコードmodel.idの作成時にファイル名をDBに保存するときにまだ利用できないという同じ問題に遭遇しました。uploaderこの回避策を見つけました。RESTful の原則を尊重しているかどうかはわかりませんが、提案を受け付けています。

イメージの作成直後に an が実行されるようにコントローラーを変更し、現在存在する値update_attributesを含むファイル名が DB に保存されるようにしました。model.id

  def create
    @uploader = Uploader.new(uploader_params)
    if @uploader.save
      if @uploader.update_attributes(uploader_params)
          render json: @uploader, status: :created 
      end
    else
      render json: @uploader.errors, status: :unprocessable_entity
    end
  end
于 2015-07-03T15:12:29.307 に答える
2

Taavo の回答は私の質問に厳密に答えます。しかし、他の人にも役立つ可能性があるため、実装した最終的な解決策について簡単に説明したいと思います...

ファイル名にモデル ID を使用するというアイデアをあきらめ、代わりにランダムな文字列に置き換えました (ファイル名のモデル ID の全体的なアイデアは、異なるモデルに関連付けられた 2 つの同一のファイルが異なるファイル名になるようにすることでした)。 ; そしていくつかのランダムな文字も同様に保証します)。

だから私はのようなファイル名になりましたfilehash-randomstring.extension

Carrierwave はファイル名をモデルに保存するため、既にモデルで使用可能なファイル ハッシュがあることに気付きました (ファイル名の最初の部分の形式で)。だから私store_dirはフォーム内のパスを生成するためにこれを使用しましたmodel_class_name/file_hash_part/another_file_hash_part

私の最終的な実装は次のようになります。

class MyUploader < Carrierwave::Uploader::Base

  def store_dir

    # file name saved on the model. It is in the form:
    # filehash-randomstring.extension, see below...
    filename = model.send(:"#{mounted_as}_identifier")

    "#{model.class.name.underscore}/#{filename[0..1]}/#{filename[3..4]}"
  end

  def filename
    if original_filename

      existing = model.send(:"#{mounted_as}_identifier")

      # reuse the existing file name from the model if present.
      # otherwise, generate a new one (and cache it in an instance variable)
      @generated_filename ||= if existing.present?
        existing
      else
        "#{sha1_for file}-#{SecureRandom.hex(4)}.#{file.extension}"
      end

    end
  end

  private

    def sha1_for file
      Digest::SHA1.hexdigest file.read
    end

end
于 2013-08-28T12:04:20.057 に答える