2012-06-22 64 views
8

我成立了一個story模型由回形針處理的image附件後的「新」行動,看起來像文件路徑:回形針(3.0)和Rails(3.2):f.file_field失去一個驗證錯誤

class Story < ActiveRecord::Base 
    has_attached_file :image # [...] 
    attr_accessible :user_id, :title, :image, :image_file_name 
    belongs_to: user 

    validates_presence_of :user_id 
    validates :title,  :length => { :maximum => 50 } 
    validates_attachment_size :image, :less_than => 2.megabytes, :unless => Proc.new { |story| story[:image].nil? } 
    # [...] 
end 

當我填寫我的故事的形式,看起來像:

<%= form_for @story, html: { multipart: true } do |f| %> 
<% if @story.errors.any? %> 
<div id="error-explanation"> 
    <ul> 
     <% @story.errors.full_messages.each do |msg| %> 
     <li class="error-mess">Error: <%= msg.downcase %></li> 
     <% end %> 
    </ul> 
</div> 
<% end %> 

<%= f.text_field :title %></td> 
<%= f.file_field :image %> 
<%= f.submit t('.send') %> 

<% end %> 

如果驗證了story.title過長形式沿着正確與正確的錯誤消息無效TITL重新顯示失敗e已經填寫,,但file_field現在是空白的,我必須再次點擊它才能重新選擇我想上傳的文件

這裏是我的stories_controller.rb的樣子:

def create 
    @story = @current_user.stories.new(params[:story]) 
    if @story.save                             
    redirect_to thanks_path  
    else 
    # [email protected] so I render action 'new' again just to 
    # bang my head against this 'anomaly' 
    render action: "new" 
    end    
end 

我怎樣才能避免重新選擇文件驗證錯誤後上傳用戶?

+0

我面臨同樣的問題,你有沒有找到一個解決辦法?謝謝! – yorch

+0

只有一個圖形:選擇圖片時,我通過jQuery提供UI反饋。假設我的圖像字段是由'<%= f.file_field:image%>生成的,我可以使用下面的jQuery來觸發它:'$(「#story_image」)。change(function(){$(「#image_selected 「).show(300);});'然後在我看來,我有一個隱藏的div」image_selected「,其中包含一個雄辯的gif ...如果您發現_real_解決方案,請告訴我! – Darme

回答

5

HTTP文件上傳在瀏覽器中的工作方式是,該文件在第一次提交時已經上傳到您的應用程序 - 所以您應該將它存儲在某個地方,以便您稍後可以在第二次提交表單時訪問它。 (至少在PHP中,腳本運行後上傳的文件被刪除,如果它沒有明確移動到其他地方 - 我不知道是否也適用於RoR。)

不能 pre - 爲了安全起見,在HTML中填充輸入類型=文件字段。即使用戶再次選擇文件,他們也必須再次發送 - 浪費用戶和您的帶寬。

因此,無論什麼地方存放起來先提交,或嘗試允許提交之前做你的客戶端用JavaScript也驗證(儘可能),這樣就最大限度地減少表單提交的實際驗證失敗在服務器端。

+2

好的,現在的問題是:如何在RoR應用程序中檢索上一個請求中上傳的文件? :-)感謝您提供有關安全原因的提示,但我並沒有完全意識到這一點。 – Darme

1

CBroe是對的,最好的解決方案是臨時存儲文件。我要做的是: - 將文件移動到臨時目錄,並用試圖上傳它的用戶的ID命名。 - 當表單發佈並且沒有文件上傳時,嘗試使用該用戶的臨時文件(如果存在)。 - 如果故事成功保存,則刪除該用戶的任何臨時文件。

我認爲應該這樣做。

+1

所以問題是:我怎麼能在Rails上做到這一點? – Darme

1

我不得不在最近的一個項目中解決這個問題。這有點hacky,但它的作品。我已經嘗試在模型中使用after_validation和before_save調用cache_images(),但由於某些原因無法確定,因此我只是從控制器調用而不是創建失敗。希望這可以節省別人一些時間!

型號:

class Shop < ActiveRecord::Base  
    attr_accessor :logo_cache 

    has_attached_file :logo 

    def cache_images 
    if logo.staged? 
     if invalid? 
     FileUtils.cp(logo.queued_for_write[:original].path, logo.path(:original)) 
     @logo_cache = encrypt(logo.path(:original)) 
     end 
    else 
     if @logo_cache.present? 
     File.open(decrypt(@logo_cache)) {|f| assign_attributes(logo: f)} 
     end 
    end 
    end 

    private 

    def decrypt(data) 
    return '' unless data.present? 
    cipher = build_cipher(:decrypt, 'mypassword') 
    cipher.update(Base64.urlsafe_decode64(data).unpack('m')[0]) + cipher.final 
    end 

    def encrypt(data) 
    return '' unless data.present? 
    cipher = build_cipher(:encrypt, 'mypassword') 
    Base64.urlsafe_encode64([cipher.update(data) + cipher.final].pack('m')) 
    end 

    def build_cipher(type, password) 
    cipher = OpenSSL::Cipher::Cipher.new('DES-EDE3-CBC').send(type) 
    cipher.pkcs5_keyivgen(password) 
    cipher 
    end 

end 

控制器:

def create 
    @shop = Shop.new(shop_params) 
    @shop.user = current_user 
    @shop.cache_images 

    if @shop.save 
    redirect_to account_path, notice: 'Shop created!' 
    else 
    render :new 
    end 
end 

def update 
    @shop = current_user.shop 
    @shop.assign_attributes(shop_params) 
    @shop.cache_images 

    if @shop.save 
    redirect_to account_path, notice: 'Shop updated.' 
    else 
    render :edit 
    end 
end 

觀點:

= f.file_field :logo 
= f.hidden_field :logo_cache 

- if @shop.logo.file? 
    %img{src: @shop.logo.url, alt: ''}