Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails 3 get raw post data and write it to tmp file

I'm working on implementing Ajax-Upload for uploading photos in my Rails 3 app. The documentation says:

  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.

So, how can I receive the raw post data in my controller and write it to a tmp file so my controller can then process it? (In my case the controller is doing some image manipulation and saving to S3.)

Some additional info:

As I'm configured right now the post is passing these parameters:

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

... and the CREATE action looks like this:

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

... but I get this error:

ActiveRecord::RecordInvalid (Validation failed: File file name must be set.):
  app/controllers/attachments_controller.rb:7:in `create'
like image 646
Andrew Avatar asked Jan 16 '11 06:01

Andrew


2 Answers

That's because params[:qqfile] isn't a UploadedFile object but a String containing the file name. The content of the file is stored in the body of the request (accessible by using request.body.read). Ofcourse, you can't forget backward compatibility so you still have to support UploadedFile.

So before you can process the file in a uniform way you have to catch both cases:

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
like image 63
Stefaan Colman Avatar answered Oct 14 '22 15:10

Stefaan Colman


try it, add 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

in assets_controller type this:

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}
});
like image 20
super_p Avatar answered Oct 14 '22 16:10

super_p