1
votes

let me tell you what is going on.

I have a web client which is fully html/css/JavaScript (no server side language), and a Ruby on Rails server that consumes and receives json data.

Early everything was going fine, once I got how to solve some cross-domain problems, but now I need to include a image and it brought me troubles about sending my data through regular JQuery Ajax.

My form is based on twitter bootstrap and have simple texts fields, a file input and a button to upload the image:

    <div class="controls">
        <input class="input-file" id="fileInput" type="file" value="" required />
        <input id="file-upload-btn" type="button" value="upload"/>
    </div>

In my functions.js javascript file I just get the form params and I'm trying to use FileReader API to deal with the image file. See the following:

function create_image(file, callback) {
    var reader = new FileReader();
    reader.onload = function() { callback(reader.result) };
    reader.readAsDataURL(file);
}

As far as I know the readAsDataURL() method returns a encoded base64 stuff and there we go.

For now I just select any image file through that file input and I start what would be the upload process by JQuery Ajax Stuff:

$upload = $("#form-produto-container"); //caches the form dom element

    $upload.delegate('#file-upload-btn','click',function(e){ //fires the upload

var file = document.getElementById('fileInput').files[0]; //get the image file

create_image(file, function(result) {
        var img = result.replace(/^data:image\/[^;]/, 'data:application/octet-stream');

    $(".fileupload-new.thumbnail img").attr('src',img); //display thumbnail
    });
    //.... goes on 'till the reach the ajax which I'll explain later

'Till that point the thing works like a charm once I just can get the encoded file data, split it's metadata, change it and set it into the src attribute of my thumbnail img tag, once I fire the the upload button.

Now the thing becomes serious. I need to send all data into a json well formated object to my rails rest service, and it goes like this :

$submmit.on("click",function(e){ // fires ajax sending the whole data
    e.preventDefault();

    //...here I get all data into form fileds using JQuery selectors and set it into vars
    //considering that I already got the params I can set my json object like following
    // REMEMBER:the img var contains the image data previously added through FileReader

    dataproduct = { "product":{"name": name, "price" : price, "description":desc,  "place" : place, "tag_list" : array_tags, "category_ids" : category_ids, "image" : img  }};

    $.ajax({
    type: 'post',
    url: rootUrl+'/ajax_products.json',
    data: dataproduct,
        success: function(data){
            $msg.addClass("alert alert-success");
    $msg.html("Your product was succsessfuly added !!!");
    },
    error: function(jqxhr){
        console.log(jqxhr);
        },
}); //ajax end
 }); // submmit end

As you could see it's pretty standard ajax sending, nothing to die for.

In my Rails side I have : Model:

class Product < ActiveRecord::Base
 attr_accessible :name, :price, :category_ids, :tag_list, 
 :place, :description,  :image 
 has_and_belongs_to_many :categories
 acts_as_taggable
 validates :name, :price, :category_ids, :tag_list, 
:place, :description, :presence =>   true

has_attached_file :image, styles: {
 thumb: '100x100>',
 square: '200x200#',
 medium: '300x300>'
},
:storage => :s3,
:s3_credentials => "#{Rails.root}/config/s3.yml",
:path => ":attachment/:id/:style.:extension",
:bucket => 'serverassets'

require 'tempfile'

def set_picture(data)

 file = Tempfile.new(['test', '.jpg']) 

 begin
   file.binmode
   file.write(data)
   self.image = file
 ensure
   file.close
   file.unlink
 end
end

end

The set_picture method receives data which is the supposedly raw image file. It creates a temp file and writes data content into it and set int into self.image attribute.

Now the controller :

  def ajax_product
    @product = Product.new
    uploaded_file = @product.set_picture(params[:product][:image])
    params[:product][:image] = uploaded_file
    @product.attributes = params[:product]

respond_to do |format|
  if @product.save
    format.json { render json: @product, status: :created, location: @product }
  else
    format.json { render json: @product.errors, status: :unprocessable_entity }
  end
end

end

In this controller I just catch the file data related to the incoming request and process it trough set_picture model's method into uploaded_file local variable. Finally I re-set the params[:product][:image] original hash value.

Paperclip settings and S3 amazon integration are pretty standard but totally functional inside rails app itself everything works perfectly once I use a simple erb multipart/form-data and just the default form submit sending.

If I send the dataproduct json without the image param, everything works fine also. But when it goes with the img param I got that errors in my console !!!

Command :: identify -format '%wx%h,%[exif:orientation]' '/tmp/test20130609-4953-      aqzvpm20130609-4953-12ijmo6.jpg[0]'
[paperclip] An error was received while processing:     #<Paperclip::Errors::NotIdentifiedByImageMagickError:     Paperclip::Errors::NotIdentifiedByImageMagickError>

I've been reading lot of issues about cocaine and identify and this things are working by itselves, by the way if I run Paperclip.run("identify", "-format '%wx%h,%[exif:orientation]' :file", :file => "/tmp/test20130609-4953- aqzvpm20130609-4953-12ijmo6.jpg[0").

I get: identify: Not a JPEG file: starts with 0x30 0x30 '/tmp/test20130609-4953- aqzvpm20130609-4953-12ijmo6.jpg[0' @ error/jpeg.c/EmitMessage/236.

So I think that must be a way to kinda "reconstruct" my original valid jpg encoding file into rails application, not completely sure about that, but I'm working on it.

Wow guys, sorry for that long, I've been trying lots of things such as adding different contentType and processData ajax settings, also, other FileReader methods like readAsArrayBuffer() and readAsText(), sending the file without the splited metadata and methods to different character encoding, utf-8, ascII, base64 and so on and on on...none of this worked but I'm really tired and I would really appreciate some help x.x

4

4 Answers

1
votes

One, the command you're running to test the convert is missing characters. You pasted "/tmp/test20130609-4953- aqzvpm20130609-4953-12ijmo6.jpg[0" which does not have an ending ], so I wouldn't expect that to have worked.

Two, I would ask if it works if you add that ] but I know it won't: You said you Base64 encode it on the JS side with FileReader but you don't ever say that you Base64 decode it. In your set_picture method, you should decode the data. That will be closer to get you what you need. You can also look at the file rather than just send it through convert to make sure that it survived the trip through the server.

0
votes

Not able to under stand the actual problem. Seems like you want to send the file data using ajax file upload plugin. Ajax file file upload sends the unserialized data using submitting a hidden i-frame. Please try once with out ajax file upload and normal form submission and check in the console the actual error.

0
votes

I see in your stack trace you are using meta_request. We had a similar problem with this gem and jQuery upload. Try getting it to work without meta_request. Then you may want to look at fixing meta request or opening a ticket here https://github.com/dejan/rails_panel/issues.

0
votes

Actually I got this a long time ago, but just now I'll put the snippet that solved it based on jyurek tips.

require 'tempfile'

def set_picture(data)

    file = Tempfile.new(['test', '.jpg']) 

    begin
        file.binmode
        file.write(ActiveSupport::Base64.decode64(data)) #did the trick
        self.image = file
    ensure
        file.close
        file.unlink
    end
end