将文件发送到Rails JSON API

我知道有类似的问题,但我还没有找到一个好的答案。 我需要做的是将一个对象的描述发送到我的一个创建方法,其中包括一些不同的属性,包括一个名为:image,paperclip attachment:

has_attached_file :image 

现在我已经读过,通过将图像编码和解码为base64,可以直接在JSON中完成发送图像,但这对我来说感觉就像一个肮脏的解决方案。 必须有更好的方法。

另一个解决方案是发送multipart / form-data请求,就像LEEjava在这里描述的那样。 这个的问题是在Rails 3.2.2中没有正确解释请求参数,并且JSON.parse在尝试解析params时吐出错误,或者可能是错误解释某些东西的Rails。

在2012-03-18 15:53:30开始POST“/api/v1/somemodel.json?token=ZoipX7yhcGfrWauoGyog”for 127.0.0.1 + 0200由Api :: V1处理:: SomeController #create as JSON参数:{“ {\ n
\“parentmodel \”:{\ n \“superparent_id \”:1,\ n
\“描述\”:\“享受花朵”,\ n \“\”:“=> {”\ n
{\ n \“someattribute \”:1,\ n
\“someotherattribute \”:2,\ n \“image \”:\“image1 \”\ n
} \ n“=> {”\ n} \ n}“=> nil}},”token“=>”ZoipX7yhcGfrWauoGyog“}

抱歉,这很难读。 JSON.parse(params [:parentmodel])在这里是不可能的,我不能JSON.parse(params),因为令牌属性,JSON.parse(params)抛出此错误:

TypeError(无法将ActiveSupport :: HashWithIndifferentAccess转换为String)

这让我相信我要么完全错误地接近这个问题,要么我只是在做某事。 无论哪种方式,我们都可以确定我对某事感到错误。 🙂

有一个更好的方法吗? 有人能指点我任何指南/教程,或写一个答案描述我应如何处理这个问题?

先感谢您

更新:所以我现在实际上已经开始工作,但仅限于测试。 我不完全确定这是如何工作的,但也许有人可以为我填补空白? 这是测试代码的一部分(图像:fixture_file_upload(…)是重要的部分)。

 parts_of_the_object = { someattribute: 0, someotherattribute: 0, image: fixture_file_upload('/images/plot.jpg', 'image/jpg') } 

我的params []看起来像提交的普通HTML表单,这很奇怪(而且很棒):

 Parameters: {"superparentid"=>"1", "plots"=>[{"someattribute"=>"0", "someotherattribute"=>"0", "image"=>#<ActionDispatch::Http::UploadedFile:0x007f812eab00e8 @original_filename="plot.jpg", @content_type="image/jpg", @headers="Content-Disposition: form-data; name=\"plots[][image]\"; filename=\"plot.jpg\"\r\nContent-Type: image/jpg\r\nContent-Length: 51818\r\n", @tempfile=#>}], "token"=>"4L5LszuXQMY6rExfifio"} 

请求就像使用rspec发出请求一样:

 post "/api/v1/mycontroller.json?token=#{@token}", thefull_object 

所以我已经完成了所有工作。 我只是不知道它是如何工作的! 我希望能够自己创建这样的响应,不仅仅是来自RSpec。 🙂

昨天我实际上在这个问题上度过了一段可怕的时间来做一些非常相似的事情。 事实上,我写了一个问题: Base64从Android / Java上传到RoR Carrierwave

它归结为在控制器中创建上传的图像对象,然后将其注入params。

对于这个特定的例子,我们正在采用base64文件(我假设你有,因为JSON不支持嵌入文件)并将其保存为系统中的临时文件,然后我们创建了UploadedFile对象并最终将其重新注入到PARAMS。

我的json / params看起来像什么:

 picture {:user_id => "1", :folder_id => 1, etc., :picture_path {:file => "base64 awesomeness", :original_filename => "my file name", :filename => "my file name"}} 

这是我的控制器现在的样子:

  # POST /pictures # POST /pictures.json def create #check if file is within picture_path if params[:picture][:picture_path]["file"] picture_path_params = params[:picture][:picture_path] #create a new tempfile named fileupload tempfile = Tempfile.new("fileupload") tempfile.binmode #get the file and decode it with base64 then write it to the tempfile tempfile.write(Base64.decode64(picture_path_params["file"])) #create a new uploaded file uploaded_file = ActionDispatch::Http::UploadedFile.new(:tempfile => tempfile, :filename => picture_path_params["filename"], :original_filename => picture_path_params["original_filename"]) #replace picture_path with the new uploaded file params[:picture][:picture_path] = uploaded_file end @picture = Picture.new(params[:picture]) respond_to do |format| if @picture.save format.html { redirect_to @picture, notice: 'Picture was successfully created.' } format.json { render json: @picture, status: :created, location: @picture } else format.html { render action: "new" } format.json { render json: @picture.errors, status: :unprocessable_entity } end end end 

此时唯一要做的就是删除临时文件,我相信可以使用tempfile.delete

我希望这有助于你的问题! 昨天我整天都在寻找解决方案,而我所看到的一切都是死路一条。 但是,这适用于我的测试用例。

TomJ给出了一个很好的答案,但至少在Rails 3 / Ruby 1.9中有一些小漏洞。

首先,不要尝试在params对象中调用可能是UploadedFile对象的[]。 例如,确保先检查它.is_a?(Hash)

另外,请确保在编写后使用tempfile.rewind() ,否则将获得长度为0的文件。

:original_filename UploadedFile构造函数的参数中的:original_filename键是不必要/未使用的。 另一方面,您可能想要提供:type键。 找到类型值的简单方法是mime_type = Mime::Type.lookup_by_extension(File.extname(original_filename)[1..-1]).to_s

这是一个应用了更改的版本:

 # POST /pictures # POST /pictures.json def create #check if file is within picture_path if params[:picture][:picture_path].is_a?(Hash) picture_path_params = params[:picture][:picture_path] #create a new tempfile named fileupload tempfile = Tempfile.new("fileupload") tempfile.binmode #get the file and decode it with base64 then write it to the tempfile tempfile.write(Base64.decode64(picture_path_params["file"])) tempfile.rewind() mime_type = Mime::Type.lookup_by_extension(File.extname(original_filename)[1..-1]).to_s #create a new uploaded file uploaded_file = ActionDispatch::Http::UploadedFile.new( :tempfile => tempfile, :filename => picture_path_params["filename"], :type => mime_type) #replace picture_path with the new uploaded file params[:picture][:picture_path] = uploaded_file end @picture = Picture.new(params[:picture]) respond_to do |format| if @picture.save format.html { redirect_to @picture, notice: 'Picture was successfully created.' } format.json { render json: @picture, status: :created, location: @picture } else format.html { render action: "new" } format.json { render json: @picture.errors, status: :unprocessable_entity } end end 

结束

如果您使用的是载波,那么有一个很棒的gem

https://github.com/lebedev-yury/carrierwave-base64