2011-01-16 9 views
13

Pracuję nad wdrożeniem Ajax-Upload do przesyłania zdjęć w mojej aplikacji Rails 3. Dokumentacja mówi:Szyny 3 otrzymują surowe dane pocztowe i zapisują je do pliku 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.

Tak, jak mogę otrzymać surowych danych post w moim kontrolera i zapisać go do pliku tmp więc mój kontroler może następnie przetwarzać je? (W moim przypadku sterownik jest jakiejś manipulacji obrazu i zapisywania na S3.)

pewne dodatkowe informacje:

Jak mam skonfigurowane teraz post przechodzi tych parametrów:

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

... i CREATE działania wygląda następująco:

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 

... ale otrzymuję ten błąd:

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

Właśnie wydałem przykład ładowania ajax w szynach 3 + Uploadify tutaj: https://github.com/apneadiving/Pic-upload---Crop-in-Ajax. Mam nadzieję, że to pomoże – apneadiving

Odpowiedz

26

To dlatego, że parametry [: qqfile] nie są obiektem UploadedFile, ale ciągiem zawierającym nazwę pliku. Zawartość pliku jest przechowywana w treści żądania (dostępnego przy użyciu adresu request.body.read). Oczywiście, nie możesz zapomnieć o kompatybilności wstecznej, więc nadal musisz obsługiwać UploadedFile.

Więc zanim będzie można przetworzyć plik w jednolity sposób trzeba złapać oba przypadki:

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

Dzięki! To działało świetnie! Jako komentarz, znalazłem (po ustawieniu mnie na właściwym torze), że w moim przypadku przynajmniej 'tmp_file = Tempfile.new (nazwa pliku)' działa równie dobrze będąc trochę czystszym niż twój kod pod '# Creating a temp uwaga pliku. Oba sposoby działają dobrze. Dzięki! – Andrew

+0

'f.write params [: qqfile] .read' - powinno to być' f.write params [: qqfile] .tempfile.read' w przeciwnym razie nie zostanie zapisany żaden plik – Toshe

+0

Nie, nie powinien, UploadedFile # read jest skrótem do UploadedFile # tempfile.read, więc kod robi to samo w obu przypadkach. –

5

spróbować, dodać 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 

tego typu assets_controller :

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

Innym rozwiązaniem jest:

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

oraz w config.ru:

require 'rack/raw_upload' 
use Rack::RawUpload 

i korzystać params [: plik] w sterowniku.