Отправка файлов в Rails JSON API - PullRequest
29 голосов
/ 18 марта 2012

Я знаю, что есть вопросы, похожие на этот, но я еще не нашел хорошего ответа.Что мне нужно сделать, это отправить описание объекта в один из моих методов создания, который включает в себя несколько различных атрибутов, включая один из них называется: изображение, вложение скрепки:

has_attached_file :image

Теперь я прочитал эту отправкуизображение можно сделать прямо в JSON, кодируя и декодируя изображение как base64, но для меня это выглядит грязным решением.Должны быть лучшие способы.

Другое решение - отправка запроса multipart / form-data, очень похожего на тот, который LEEjava описывает здесь. Проблема в том, что параметры запроса не являютсяинтерпретируется правильно в Rails 3.2.2, и JSON.parse выдает ошибку, когда пытается проанализировать параметры, или, возможно, именно Rails что-то неправильно интерпретирует.

Started POST "/ api / v1/somemodel.json?token=ZoipX7yhcGfrWauoGyog "для 127.0.0.1 в 2012-03-18 15:53:30 +0200 Обработка Api :: V1 :: SomeController # создать как параметры JSON: {" {\ n
\"parentmodel \": {\ n \ "superparent_id \": 1, \ n
\ "description \": \ "Наслаждайся цветком \", \ 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') }

Мои параметры [] выглядят так, как будто была отправлена ​​нормальная форма 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.: -)

Ответы [ 3 ]

40 голосов
/ 25 марта 2012

Вчера у меня был ужасный момент с этим вопросом, чтобы сделать что-то очень похожее.На самом деле, я написал вопрос: Загрузка Base64 из Android / Java в RoR Carrierwave

В результате получилось создать этот загруженный объект изображения в контроллере, а затем вставить его обратно вТитулы.

Для этого конкретного примера мы берем файл 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

Надеюсь, это поможет с вашим вопросом!Я провел весь день в поисках решения вчера, и все, что я видел, - тупик.Это, однако, работает на моих тестовых случаях.

9 голосов
/ 03 января 2013

TomJ дал хороший ответ, но, по крайней мере, в Rails 3 / Ruby 1.9 есть небольшие пробелы.

Во-первых, не пытайтесь вызывать [] для того, что может быть объектом UploadedFile в вашем объекте params. Например, сначала убедитесь, что он .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

конец

4 голосов
/ 26 марта 2015

Для этой цели есть замечательная жемчужина, если вы используете carrierwave

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...