Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>Okay, this is a complex one but it is doable. Here's how I got it to work.</p> <p>On the client side I used <a href="http://github.com/valums/file-uploader">http://github.com/valums/file-uploader</a>, a javascript library which allows multiple file uploads with progress-bar and drag-and-drop support. It's well supported, highly configurable and the basic implementation is simple:</p> <p>In the view:</p> <pre><code>&lt;div id='file-uploader'&gt;&lt;noscript&gt;&lt;p&gt;Please Enable JavaScript to use the file uploader&lt;/p&gt;&lt;/noscript&gt;&lt;/div&gt; </code></pre> <p>In the js:</p> <pre><code>var uploader = new qq.FileUploader({ element: $('#file-uploader')[0], action: 'files/upload', onComplete: function(id, fileName, responseJSON){ // callback } }); </code></pre> <p>When handed files, FileUploader posts them to the server as an XHR request where the POST body is the raw file data while the headers and filename are passed in the URL string (this is the only way to upload a file asyncronously via javascript). </p> <p>This is where it gets complicated, since Paperclip has no idea what to do with these raw requests, you have to catch and convert them back to standard files (preferably before they hit your Rails app), so that Paperclip can work it's magic. This is done with some Rack Middleware which creates a new Tempfile (remember: Heroku is read only):</p> <pre><code># 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 =&gt; env['HTTP_X_FILE_NAME'], :type =&gt; content_type(env['HTTP_X_FILE_NAME']), :tempfile =&gt; 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 </code></pre> <p>Later, in application.rb:</p> <pre><code>config.middleware.use 'Rack::RawFileStubber' </code></pre> <p>Then in the controller:</p> <pre><code> def upload @foo = modelWithPaperclip.create({ :img =&gt; params[:file] }) end </code></pre> <p>This works reliably, though it can be a slow process when uploading a lot of files simultaneously.</p> <p><strong>DISCLAIMER</strong></p> <p>This was implemented for a project with a single, known &amp; trusted back-end user. It almost certainly has some serious performance implications for a high traffic Heroku app and I have not fire tested it for security. That said, it definitely works.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload