Wysyłanie plików do API Rails JSON

Wiem, że są pytania podobne do tego, ale nie znalazłem jeszcze dobrej odpowiedzi. To, co muszę zrobić, to wysłać opis obiektu do jednej z moich metod tworzenia, który zawiera kilka różnych atrybutów, w tym jeden o nazwie: image, a paperclip attachment:

has_attached_file :image

Teraz czytałem, że wysyłanie obrazu może być wykonane prosto w JSON przez kodowanie i dekodowanie obrazu jako base64, ale wydaje mi się to brudnym rozwiązaniem. Musi być lepiej sposoby.

Innym rozwiązaniem jest wysłanie zapytania multipart / form-data, podobnie jak to opisuje tutaj LEEjava. problem z tym polega na tym, że paramy request nie są poprawnie interpretowane w Rails 3.2.2 i JSON.parse wypluwa błąd, gdy próbuje parsować params, a może to Rails coś źle interpretuje.

Rozpoczął POST " / api / v1 / somemodel.json?token=ZoipX7yhcGfrWauoGyog " dla 127.0.0.1 at 2012-03-18 15:53: 30 + 0200 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"}

Trudno to odczytać, przepraszam. JSON.parse (params[:parentmodel]) nie jest tutaj możliwe, a ja nie mogę JSON.parse (params) albo ze względu na atrybut token, JSON.parse (params) wyrzuca ten błąd:

TypeError (nie można przekonwertować Activesupport::HashWithIndifferentAccess na String)

Co prowadzi mnie do przekonania, że albo podchodzę do tego problemu całkowicie źle, albo po prostu coś robię. Tak czy siak, możemy być pewni, że się mylę. :) Czy jest na to lepszy sposób? Czy ktoś może wskazać mi jakiś poradnik/tutorial, albo napisać odpowiedź opisującą, jak mam do tego podejść?

Z góry dziękuję

Aktualizacja: Więc mam go teraz działa, ale tylko w testach. Nie jestem do końca pewien, jak to działa, ale może ktoś może wypełnić luki dla mnie? Jest to część kodu testowego (obrazek: fixture_file_upload (...) jest ważną częścią).

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

Moje params [] wygląda jak zwykły formularz HTML, co jest dziwne (i niesamowite):

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

Wniosek składa się podobnie jak i żądanie post jest dokonywane za pomocą rspec:

post "/api/v1/mycontroller.json?token=#{@token}", thefull_object
Więc wszystko działa. Po prostu nie wiem, jak to dokładnie działa! Chcę być w stanie stworzyć taką odpowiedź również sama, nie tylko ze strony RSpec. :-)
Author: Emil Ahlbäck, 2012-03-18

3 answers

Naprawdę miałem okropny czas z tym pytaniem wczoraj, aby zrobić coś bardzo podobnego. W rzeczywistości napisałem pytanie: Base64 upload from Android / Java to Ror Carrierwave

Sprowadza się to do utworzenia przesłanego obiektu obrazu w kontrolerze, a następnie wstrzyknięcia go z powrotem do params.

W tym konkretnym przykładzie bierzemy plik base64 (który zakładam, że masz, ponieważ JSON nie obsługuje osadzonych plików) i zapisujemy go jako plik tymczasowy w system następnie tworzymy obiekt UploadedFile i ponownie wstawiamy go do params.

Jak wygląda mój json/params:

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

Oto jak teraz wygląda mój kontroler:

  # 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

Jedyną rzeczą do zrobienia w tym momencie jest usunięcie pliku tempfile, który moim zdaniem można zrobić za pomocą tempfile.delete

Mam nadzieję, że to pomoże w twoim pytaniu! Spędziłem wczoraj cały dzień szukając rozwiązania, a wszystko, co widziałem, to ślepy zaułek. To jednak działa na moim teście sprawy.

 42
Author: TomJ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:29:57

TomJ dał dobrą odpowiedź, ale przynajmniej w Rails 3 / Ruby 1.9 są drobne dziury.

Po pierwsze, nie próbuj wywoływać [] obiektu UploadedFile w obiekcie params. Upewnij się, że najpierw to .is_a?(Hash), na przykład.

Również upewnij się, że tempfile.rewind() Po napisaniu, w przeciwnym razie dostaniesz pliki o długości 0.

Klucz :original_filename w parametrach do konstruktora UploadedFile jest zbędny/nieużywany. Z drugiej strony, możesz chcieć zapewnić klucz :type. Łatwy sposób na znalezienie wartości dla typu to mime_type = Mime::Type.lookup_by_extension(File.extname(original_filename)[1..-1]).to_s

Oto wersja z zastosowanymi zmianami:

# 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

End

 9
Author: Shannon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-02 21:57:09

Istnieje niesamowity klejnot do tego celu, jeśli używasz carrierwave

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

 5
Author: peeyush singla,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-26 09:30:43