2013-08-27 41 views
3

我在一個Rails 4項目中使用carrierwave,用於開發和測試的文件存儲以及用於生產的霧存儲(用於存儲在Amazon S3上)。Carrierwave:文件哈希和模型ID在文件名/ store_dir

我想保存這樣的路徑,我的文件:

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

(例如:/images/12/34/1234567-89.png,其中1234567是文件內容的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.id時不可filename
  • file並不總是可用時store_dir是叫做

所以,來我的問題

  • 是有可能使用模型IDS /內filename屬性? This link表示不應該這樣做;有沒有辦法解決它?
  • 是否可以使用store_dir內的文件內容/屬性?我沒有找到有關這方面的文件,但我迄今的經驗說「不」(見上文)。
  • 你將如何實現文件/目錄命名,以儘可能接近我在開始概述的內容?

回答

4
  • 包括上創建可能是不可能的,因爲文件名存儲在數據庫中,但該ID還沒有提供文件名中的ID。一種(不可否認的極端)解決方法是在創建時使用臨時值,然後移動文件並更改數據庫中的名稱。可以使用after_create來優化它,但是我會把它留給你。 (This是其中carrierwave實際上傳文件。)

  • 包括文件屬性直接store_dir內不可能的,因爲store_dir被用來計算url - url將需要知道SHA1,這需要具有訪問到需要知道url的文件,等等。解決方法很明顯:緩存模型數據庫記錄中感興趣的屬性(在本例中爲sha1),並在store_dir中使用該屬性。

  • id-in-filename方法的簡單變體是使用其他值,比如uuid,並將該值存儲在數據庫中。有一些筆記here

+0

我接受了你的回答,因爲它回答了我的問題中的每一點,謝謝。如果您對我的最終解決方案感興趣,請查看我自己的答案,詳細說明它... – severin

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 
2

我碰到同樣的問題,最近,這裏的model.id存儲在數據庫中的文件名時,在創建uploader記錄的是尚未公佈出來。我發現這個解決方法。我不確定它是否尊重RESTful原則,我願意接受建議。

我修改了控制器,以便在創建圖像後立即執行update_attributes,以便將包含現有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