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.
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: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"}
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ść?TypeError (nie można przekonwertować Activesupport::HashWithIndifferentAccess na String)
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. :-) 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.
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
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
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