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