Ok, questo è complesso ma è fattibile. Ecco come ho potuto farlo funzionare.
Sul lato client ho utilizzato http://github.com/valums/file-uploader, una libreria javascript che consente più caricamenti di file con barra di avanzamento e supporto drag-and-drop. E 'ben supportato, altamente configurabile e l'implementazione di base è semplice:
Nella vista:
<div id='file-uploader'><noscript><p>Please Enable JavaScript to use the file uploader</p></noscript></div>
Negli js:
var uploader = new qq.FileUploader({
element: $('#file-uploader')[0],
action: 'files/upload',
onComplete: function(id, fileName, responseJSON){
// callback
}
});
Quando i file handed, FileUploader loro messaggi al server come una richiesta XHR in cui il corpo POST è i dati del file raw mentre le intestazioni e il nome file vengono passati nella stringa URL (questo è l'unico modo per caricare un file in modo asincrono tramite javascript).
Ecco dove diventa complicato, poiché Paperclip non ha idea di cosa fare con queste richieste non elaborate, devi catturarle e convertirle di nuovo in file standard (preferibilmente prima che colpiscano la tua app Rails), in modo che Paperclip possa funzionare è magico.Questo viene fatto con un po 'di Middleware Rack che crea un nuovo File Temporanei (ricordate: Heroku è di sola lettura):
# Embarrassing note: This code was adapted from an example I found somewhere online
# if you recoginize any of it please let me know so I pass credit.
module Rack
class RawFileStubber
def initialize(app, path=/files\/upload/) # change for your route, careful.
@app, @path = app, path
end
def call(env)
if env["PATH_INFO"] =~ @path
convert_and_pass_on(env)
end
@app.call(env)
end
def convert_and_pass_on(env)
tempfile = env['rack.input'].to_tempfile
fake_file = {
:filename => env['HTTP_X_FILE_NAME'],
:type => content_type(env['HTTP_X_FILE_NAME']),
:tempfile => tempfile
}
env['rack.request.form_input'] = env['rack.input']
env['rack.request.form_hash'] ||= {}
env['rack.request.query_hash'] ||= {}
env['rack.request.form_hash']['file'] = fake_file
env['rack.request.query_hash']['file'] = fake_file
if query_params = env['HTTP_X_QUERY_PARAMS']
require 'json'
params = JSON.parse(query_params)
env['rack.request.form_hash'].merge!(params)
env['rack.request.query_hash'].merge!(params)
end
end
def content_type(filename)
case type = (filename.to_s.match(/\.(\w+)$/)[1] rescue "octet-stream").downcase
when %r"jp(e|g|eg)" then "image/jpeg"
when %r"tiff?" then "image/tiff"
when %r"png", "gif", "bmp" then "image/#{type}"
when "txt" then "text/plain"
when %r"html?" then "text/html"
when "js" then "application/js"
when "csv", "xml", "css" then "text/#{type}"
else 'application/octet-stream'
end
end
end
end
Più tardi, nel application.rb:
config.middleware.use 'Rack::RawFileStubber'
Poi nel controller:
def upload
@foo = modelWithPaperclip.create({ :img => params[:file] })
end
Questo funziona in modo affidabile, anche se può essere un processo lento durante il caricamento di un sacco di file contemporaneamente.
NEGAZIONE
Questo è stato implementato per un progetto con un singolo, noto & attendibile dall'utente back-end. Quasi certamente ha delle serie implicazioni sulle prestazioni per un'app di Heroku ad alto traffico e non l'ho testato per sicurezza. Detto questo, funziona sicuramente.
Hai dimenticato di aggiungere. Sto caricando immagini che devono essere ridimensionate.ecco perché non posso caricare direttamente su S3. E sto usando heroku per l'hosting. grazie – user479959