web-dev-qa-db-ja.com

Rails JSON APIにファイルを送信する

これに似た質問があることは知っていますが、まだ良い答えは見つかりません。私がする必要があるのは、オブジェクトの説明を私のcreateメソッドの1つに送信することです。これには、:imageと呼ばれるものを含むいくつかの異なる属性が含まれています。

has_attached_file :image

画像をbase64としてエンコードおよびデコードすることにより、画像の送信をJSONで直接実行できることを今読みましたが、それは私にとって汚い解決策のように感じます。もっと良い方法があるはずです。

別の解決策は、multipart/form-dataリクエストを送信することです。これは、 LEEjavaがここで説明するものとよく似ています。 その1つに関する問題は、リクエストパラメータがRails3.2.2、そしてJSON.parseがparamsを解析しようとするとエラーを吐き出します。あるいは、何かを誤解しているRailsかもしれません。

開始済み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 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を文字列に変換できません)

これは私がこの問題に完全に間違って取り組んでいるのか、私が何かをしているだけなのかを私に信じさせる。いずれにせよ、私は何かが間違っていると確信することができます。 :)

これを行うより良い方法はありますか?誰かがガイド/チュートリアルを私に指摘したり、これにどのように取り組むべきかを説明した答えを書いたりできますか?

前もって感謝します

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"}

リクエストは同様に行われ、ポストリクエストはrspecで行われます。

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

だから私はそれがすべてうまくいっている。私はそれがどのように正確に機能するのか知りません! RSpecだけでなく、自分でもこのような応答を作成できるようにしたいと考えています。 :-)

30
Emil Ahlbäck

昨日、この質問で非常に似たようなことをするのにひどい時間がありました。実際、私は質問を書きました: Android/JavaからRoR CarrierwaveへのBase64アップロード

その結果、コントローラーにアップロードされた画像オブジェクトを作成し、それを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

これがあなたの質問に役立つことを願っています!私は昨日解決策を探すために一日中過ごしました、そして私が見たすべては行き止まりです。ただし、これは私のテストケースでは機能します。

40
TomJ

TomJは良い答えを出しましたが、少なくともRails 3/Ruby 1.9にはいくつかの小さな穴があります。

まず、paramsオブジェクトのUploadedFileオブジェクトの可能性があるものに対して[]を呼び出さないでください。たとえば、最初に.is_a?(Hash)であることを確認してください。

また、書き込み後は必ずtempfile.rewind()を実行してください。そうしないと、長さが0のファイルが取得されます。

UploadedFileのコンストラクターへのパラメーターの_:original_filename_キーは不要/未使用です。一方、_:type_キーを提供することもできます。 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
_

終わり

9
Shannon

キャリアウェーブを使用している場合、この目的のための素晴らしい宝石があります

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

4
peeyush singla