De acuerdo, este es complejo pero es factible. Así es como lo hice funcionar.
Del lado del cliente utilicé http://github.com/valums/file-uploader, una biblioteca de JavaScript que permite múltiples cargas de archivos con barra de progreso y soporte para arrastrar y soltar. Es bien apoyada, altamente configurable y la implementación básica es simple:
En la vista:
<div id='file-uploader'><noscript><p>Please Enable JavaScript to use the file uploader</p></noscript></div>
En el js:
var uploader = new qq.FileUploader({
element: $('#file-uploader')[0],
action: 'files/upload',
onComplete: function(id, fileName, responseJSON){
// callback
}
});
Cuando los archivos zurdos, FileUploader las publicará en el servidor como una solicitud XHR donde el cuerpo POST es el archivo de datos sin formato mientras que los encabezados y el nombre del archivo se pasan en la cadena URL (esta es la única forma de cargar un archivo asincronously a través de javascript).
Aquí es donde se complica, ya que Paperclip no tiene idea de qué hacer con estas solicitudes crudas, tiene que capturarlas y convertirlas a archivos estándar (preferiblemente antes de que lleguen a la aplicación Rails), para que Paperclip pueda funcionar Es magia.Esto se hace con un poco de Rack middleware que crea un nuevo archivo temporal (recuerda: Heroku es de sólo lectura):
# 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
Más tarde, en application.rb:
config.middleware.use 'Rack::RawFileStubber'
Luego, en el controlador:
def upload
@foo = modelWithPaperclip.create({ :img => params[:file] })
end
Esto funciona de manera confiable, aunque puede ser un proceso lento cuando se cargan muchos archivos simultáneamente.
Exención de responsabilidad
Esto se implementó para un proyecto con un único, conocido & usuario de confianza back-end. Es casi seguro que tiene algunas implicaciones graves de rendimiento para una aplicación Heroku de alto tráfico y no lo he probado para seguridad. Dicho eso, definitivamente funciona.
Se olvidó de agregar. Estoy cargando imágenes que deben cambiar de tamaño.por eso no puedo cargar directamente a S3. Y estoy usando heroku para hosting. gracias – user479959