ruby on rails - tutorial - Envío de archivos a una API JSON de Rails
ruby restful (3)
Ayer me estaba pasando muy mal con esta pregunta para hacer algo muy similar. De hecho, escribí la pregunta: Carga de Base64 desde Android / Java a RoR Carrierwave
Todo se redujo a crear ese objeto de imagen cargado en el controlador y luego volver a inyectarlo en los parámetros.
Para este ejemplo específico, estamos tomando un archivo base64 (que asumo que tienes, ya que JSON no admite archivos incrustados) y lo guardamos como un archivo temporal en el sistema, entonces estamos creando ese objeto UploadedFile y finalmente lo reinyectamos en el archivo. params
Cómo se ve mi json / params:
picture {:user_id => "1", :folder_id => 1, etc., :picture_path {:file => "base64 awesomeness", :original_filename => "my file name", :filename => "my file name"}}
Aquí es como se ve mi controlador ahora:
# 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
Lo único que queda por hacer en este punto es eliminar el archivo temp, que creo que se puede hacer con el tempfile.delete
Espero que esto ayude con su pregunta! Pasé todo el día buscando una solución ayer, y todo lo que he visto es un callejón sin salida. Esto, sin embargo, funciona en mis casos de prueba.
Sé que hay preguntas similares a esta, pero todavía no he encontrado una buena respuesta. Lo que debo hacer es enviar una descripción de un objeto a uno de mis métodos de creación, que incluye algunos atributos diferentes, incluido uno llamado: imagen, un adjunto de clip:
has_attached_file :image
Ahora he leído que el envío de la imagen se puede hacer directamente en JSON mediante la codificación y decodificación de la imagen como base64, pero para mí es una solución sucia. Debe haber mejores maneras.
Otra solución es enviar una solicitud multipart / form-data, como la que LEEjava describe aquí. El problema con eso es que los parámetros de solicitud no se interpretan correctamente en Rails 3.2.2, y JSON.parse escupe un error cuando intenta analizar los parámetros, o tal vez es Rails quien está malinterpretando algo.
Comenzó la POST "/api/v1/somemodel.json?token=ZoipX7yhcGfrWauoGyog" para 127.0.0.1 a 2012-03-18 15:53:30 +0200 Procesando por Api :: V1 :: SomeController # create as JSON Parameters: {" {/norte
/ "parentmodel /": {/ n / "superparent_id /": 1, / n
/ "descripción /": / "Disfruta de la flor /", / n / "/": "=> {" / n
{/ n / "someattribute /": 1, / n
/ "algún otro atributo /": 2, / n / "imagen /": / "imagen1 /" / n
} / n "=> {" / n} / n} "=> nil}}," token "=>" ZoipX7yhcGfrWauoGyog "}
Es muy difícil leer eso, lo siento. JSON.parse (params [: parentmodel]) no es posible aquí, y no puedo JSON.parse (params) debido al atributo token, JSON.parse (params) emite este error:
TypeError (no se puede convertir ActiveSupport :: HashWithIndifferentAccess en String)
Lo que me lleva a creer que estoy abordando este problema totalmente equivocado, o simplemente estoy haciendo algo. De cualquier manera, podemos estar seguros de que estoy equivocado en algo. :)
¿Hay una mejor manera de hacer esto? ¿Alguien puede indicarme alguna guía / tutorial o escribir una respuesta que describa cómo debo abordar esto?
Gracias de antemano
ACTUALIZACIÓN: Así que realmente lo tengo funcionando ahora, pero solo en las pruebas. No estoy totalmente seguro de cómo funciona esto, pero ¿quizás alguien pueda llenar los vacíos por mí? Esto es parte del código de prueba (la imagen: fixture_file_upload (...) es la parte importante).
parts_of_the_object = { someattribute: 0, someotherattribute: 0, image: fixture_file_upload(''/images/plot.jpg'', ''image/jpg'') }
Mis parámetros [] parece que se envió un formulario HTML normal, lo cual es extraño (y asombroso):
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"}
La solicitud se realiza como y la solicitud de publicación se realiza con rspec:
post "/api/v1/mycontroller.json?token=#{@token}", thefull_object
Así que lo tengo todo funcionando. ¡Simplemente no sé cómo funciona exactamente! Quiero ser capaz de crear una respuesta como esta por mí mismo, no solo de RSpec. :-)
Hay una gema increíble para este propósito si está utilizando carrierwave.
TomJ dio una buena respuesta, pero al menos en Rails 3 / Ruby 1.9 hay algunos agujeros menores.
Primero, no intentes llamar [] a lo que podría ser un objeto UploadedFile en tu objeto params. Asegúrese de verificar que sea .is_a?(Hash)
primero, por ejemplo.
Además, asegúrese de tempfile.rewind()
después de escribir, de lo contrario obtendrá archivos con una longitud de 0.
La clave :original_filename
en los parámetros para el constructor de UploadedFile es innecesaria / no utilizada. Por otro lado, es posible que desee proporcionar una clave de :type
. Una forma fácil de encontrar el valor para el tipo es mime_type = Mime::Type.lookup_by_extension(File.extname(original_filename)[1..-1]).to_s
Aquí hay una versión con los cambios aplicados:
# 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
fin