2011-01-16 3 views
13

Sto lavorando all'implementazione di Ajax-Upload per il caricamento di foto nella mia app Rails 3. La documentazione dice:Rails 3 ottiene i dati di posta non elaborati e li scrive nel file tmp

  1. For IE6-8, Opera, older versions of other browsers you get the file as you normally do with regular form-base uploads.

  2. For browsers which upload file with progress bar, you will need to get the raw post data and write it to the file.

Quindi, come posso ricevere i dati post prime mio controller e scrivere in un file tmp quindi il mio controller può quindi elaborare esso? (Nel mio caso il regolatore sta facendo un po 'di manipolazione delle immagini e il salvataggio a S3.)

Alcune informazioni aggiuntive:

Come sto configurato in questo momento il palo sta passando questi parametri:

Parameters: 
{"authenticity_token"=>"...", "qqfile"=>"IMG_0064.jpg"} 

... e CREATE azione è simile al seguente:

def create 
    @attachment = Attachment.new 
    @attachment.user = current_user 
    @attachment.file = params[:qqfile] 
    if @attachment.save! 
     respond_to do |format| 
      format.js { render :text => '{"success":true}' } 
     end 
    end 
end 

... ma ottengo questo errore:

ActiveRecord::RecordInvalid (Validation failed: File file name must be set.): 
    app/controllers/attachments_controller.rb:7:in `create' 
+0

Ho appena pubblicato un esempio di caricamento Ajax su rotaie 3 + Uploadify qui: https://github.com/apneadiving/Pic-upload---Crop-in-Ajax. Spero che possa aiutare – apneadiving

risposta

26

Questo perché i parametri [: qqfile] non sono un oggetto UploadedFile ma una stringa che contiene il nome del file. Il contenuto del file è memorizzato nel corpo della richiesta (accessibile utilizzando request.body.read). Ovviamente, non puoi dimenticare la compatibilità con le versioni precedenti, quindi devi ancora supportare UploadedFile.

Quindi, prima di poter elaborare il file in modo uniforme devi prendere entrambi i casi:

def create 
    ajax_upload = params[:qqfile].is_a?(String) 
    filename = ajax_upload ? params[:qqfile] : params[:qqfile].original_filename 
    extension = filename.split('.').last 
    # Creating a temp file 
    tmp_file = "#{Rails.root}/tmp/uploaded.#{extension}" 
    id = 0 
    while File.exists?(tmp_file) do 
    tmp_file = "#{Rails.root}/tmp/uploaded-#{id}.#{extension}"   
    id += 1 
    end 
    # Save to temp file 
    File.open(tmp_file, 'wb') do |f| 
    if ajax_upload 
     f.write request.body.read 
    else 
     f.write params[:qqfile].read 
    end 
    end 
    # Now you can do your own stuff 
end 
+4

Grazie! Questo ha funzionato alla grande! Come commento, ho trovato (dopo che mi hai impostato sulla traccia giusta) che nel mio caso almeno 'tmp_file = Tempfile.new (filename)' funziona ugualmente bene pur essendo un po 'più pulito del tuo codice sotto il '# Creazione di un temp file' note. Entrambe le strade funzionano bene. Grazie! – Andrew

+0

'f.write params [: qqfile] .read' - questo dovrebbe essere' f.write params [: qqfile] .tempfile.read' altrimenti non viene memorizzato alcun file – Toshe

+0

No, non dovrebbe, UploadedFile # read è una scorciatoia per UploadedFile # tempfile.read in modo che il codice faccia lo stesso in entrambi i casi –

5

provarlo, aggiungere lib/qq_file.rb:

# encoding: utf-8 
require 'digest/sha1' 
require 'mime/types' 

# Usage (paperclip example) 
# @asset.data = QqFile.new(params[:qqfile], request) 
class QqFile < ::Tempfile 

    def initialize(filename, request, tmpdir = Dir::tmpdir) 
    @original_filename = filename 
    @request = request 

    super Digest::SHA1.hexdigest(filename), tmpdir 
    fetch 
    end 

    def self.parse(*args) 
    return args.first unless args.first.is_a?(String) 
    new(*args) 
    end 

    def fetch 
    self.write @request.raw_post 
    self.rewind 
    self 
    end 

    def original_filename 
    @original_filename 
    end 

    def content_type 
    types = MIME::Types.type_for(@request.content_type) 
     types.empty? ? @request.content_type : types.first.to_s 
    end 
end 

nel tipo assets_controller questo :

def create 
    @asset ||= Asset.new(params[:asset]) 

    @asset.assetable_type = params[:assetable_type] 
    @asset.assetable_id = params[:assetable_id] || 0 
    @asset.guid = params[:guid] 
    @asset.data = QqFile.parse(params[:qqfile], request) 
    @asset.user_id = 0 
    @success = @asset.save 

    respond_with(@asset) do |format| 
    format.html { render :text => "{'success':#{@success}}" } 
    format.xml { render :xml => @asset.to_xml } 
    format.js { render :text => "{'success':#{@success}}"} 
    format.json { render :json => {:success => @success} } 
    end 
end 

javascript:

var photo_uploader = new qq.FileUploader({ 
    element: document.getElementById('photo-button'), 
    multiple: true, 
    action: '/assets', 
    allowedExtensions: ['png', 'gif', 'jpg', 'jpeg'], 
    sizeLimit: 2097152, 
    params: {guid: $('#idea_guid').val(), assetable_type: 'Idea', klass: 'Picture', collection: true} 
}); 
1

Un'altra soluzione è:

gem 'rack-raw-upload', :git => 'git://github.com/tb/rack-raw-upload.git' 

e in config.ru:

require 'rack/raw_upload' 
use Rack::RawUpload 

e utilizzare params [: File] del controller.