2012-03-18 43 views
27

我知道有类似的问题,但我还没有找到一个好的答案呢。我需要做的是发送对象的描述,我的创建方法之一,其中包括一些不同的属性,包括一个名为:图像,回形针附件:发送文件到Rails JSON API

has_attached_file :image 

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

另一种解决方案是发送一个多部分/表单数据请求,很像这样一个LEEjava describes here.该问题在于请求参数在Rails 3.2.2中没有被正确解释,并且JSON.parse在出现错误时会吐出一个错误它试图解析这些参数,或者是Rails误解了某些东西。

Started POST "/api/v1/somemodel.json?token=ZoipX7yhcGfrWauoGyog" for 127.0.0.1 at 2012-03-18 15:53:30 +0200 Processing by Api::V1::SomeController#create as JSON Parameters: {"{\n
\"parentmodel\": {\n \"superparent_id\": 1,\n
\"description\": \"Enjoy the flower\",\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 (can't convert ActiveSupport::HashWithIndifferentAccess into String)

这导致我相信我要么完全错误地接近这个问题,要么我只是在做一些事情。无论哪种方式,我们都可以确定我对某件事有错。 :)

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

预先感谢您

UPDATE: 所以我实际上已经得到了它现在的工作,但只有在测试。我不完全确定这是如何工作的,但也许有人可以填补我的空白?这是测试代码的一部分(图像: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=#<File:/var/folders/45/rcdbb3p50bl2rgjzqp3f0grw0000gn/T/RackMultipart20120318-1242-1cn036o>>}], "token"=>"4L5LszuXQMY6rExfifio"} 

的请求时只是想和POST请求与rspec的制作:

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

所以我已经得到了一切工作。我只是不知道它是如何工作的!我希望能够自己创建一个这样的响应,而不仅仅是RSpec。 :-)

回答

38

昨天我在这个问题上实际上遇到了很糟糕的事情,要做一些非常相似的事情。事实上,我写了这样一个问题:Base64 upload from Android/Java to RoR Carrierwave

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

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

什么的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

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

+0

让我知道如果它不能在你的dev/prod模式下工作,我前几天找到了这个解决方案,但是forg不要写答案。它和你的非常相似,只是我在模型中做了这个(把Base64数据直接发送到image_data属性中)。我更喜欢你的解决方案。让我知道它是怎么回事,如果可以的话,我会帮忙的。 – 2012-03-26 10:33:27

+0

从我所看到的答案是正确的,所以我颁发奖金。 – 2012-03-26 10:33:50

+0

这是一个很好的答案,但至少在Rails 3/Ruby 1.9中有一些小问题。 首先,在写完后确保你有'tempfile.rewind()',否则你会得到长度为0的文件。 – Shannon 2013-01-02 21:41:52

8

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

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

此外,请确保您在写入之后tempfile.rewind(),否则您将获得长度为0的文件。

:original_filename对UploadedFile的构造函数的参数键是不必要的/未使用的。另一方面,您可能需要提供一个: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 

+0

{:avatar => [“Paperclip :: Errors :: NotIdentifiedByImageMagickError”, “Paperclip :: Errors :: NotIdentifiedByImageMagickError”]}>任何想法? – ajbraus 2014-04-24 01:08:11