Подтвердить что ты не робот

Отправка файлов в Rails JSON API

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

has_attached_file :image

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

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

Запущен POST "/api/v1/somemodel.json?token=ZoipX7yhcGfrWauoGyog" для 127.0.0.1 в 2012-03-18 15:53:30 +0200 Обработка Api:: V1:: SomeController # create as 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}}, "токен" = > "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') }

Мои параметры [] выглядят как обычная форма 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 post выполняется с помощью rspec:

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

Итак, у меня все работает. Я просто не знаю, как это работает! Я тоже хочу создать такой ответ, не только от RSpec.: -)

4b9b3361

Ответ 1

У меня действительно было ужасное время с этим вопросом вчера, чтобы сделать что-то очень похожее. На самом деле, я написал вопрос: Загрузка 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

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

Ответ 2

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

конец