Je sais qu'il y a des questions similaires à celle-ci, mais je n'ai pas encore trouvé de bonne réponse. Ce que je dois faire, c'est envoyer une description d'un objet à l'une de mes méthodes de création, qui comprend différents attributs, dont un appelé: image, un attachement Paperclip:
has_attached_file :image
Maintenant, j'ai lu que l'envoi de l'image pouvait se faire directement en JSON en encodant et décodant l'image en base64, mais cela me semble une sale solution. Il doit y avoir de meilleures façons.
Une autre solution consiste à envoyer une requête multipart/form-data, un peu comme celle LEEjava décrit ici. Le problème avec celle-ci est que les paramètres de requête ne sont pas interprétés correctement dans Rails 3.2.2 et JSON.parse crache une erreur lorsqu'il essaie d'analyser les paramètres, ou c'est peut-être Rails qui interprète mal quelque chose.
Démarré POST "/api/v1/somemodel.json?token=ZoipX7yhcGfrWauoGyog" pour 127.0.0.1 à 2012-03-18 15:53:30 +0200 Traitement par Api :: V1 :: SomeController # créer en JSON Paramètres: {"{\ n
\"parentmodel \": {\ n\"superparent_id \": 1,\n
\"description \":\"Profitez de la fleur \",\n\"\": "=> {"\n
{\ n\"someattribute \": 1,\n
\"someotherattribute \": 2,\n\"image \":\"image1 \"\n
}\n "=> {"\n}\n} "=> nil}}," token "=>" ZoipX7yhcGfrWauoGyog "}
C'est assez difficile à lire, désolé. JSON.parse (params [: parentmodel]) n'est pas possible ici, et je ne peux pas non plus JSON.parse (params) en raison de l'attribut token, JSON.parse (params) renvoie cette erreur:
TypeError (impossible de convertir ActiveSupport :: HashWithIndifferentAccess en chaîne)
Ce qui m'amène à croire que j'aborde ce problème totalement mal ou que je fais juste quelque chose. Quoi qu'il en soit, nous pouvons être sûrs que je me trompe sur quelque chose. :)
Y a-t-il une meilleure manière de faire cela? Quelqu'un peut-il me diriger vers un guide/tutoriel ou écrire une réponse décrivant comment je dois aborder cela?
Merci d'avance
MISE À JOUR: Donc, je l'ai fait fonctionner maintenant, mais seulement dans les tests. Je ne sais pas trop comment cela fonctionne, mais peut-être que quelqu'un peut combler les lacunes pour moi? Cela fait partie du code de test (l'image: fixture_file_upload (...) est la partie importante).
parts_of_the_object = { someattribute: 0, someotherattribute: 0, image: fixture_file_upload('/images/plot.jpg', 'image/jpg') }
Mes paramètres [] ressemblent à un formulaire HTML normal qui a été soumis, ce qui est étrange (et génial):
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 demande est faite comme et la demande de post est faite avec rspec:
post "/api/v1/mycontroller.json?token=#{@token}", thefull_object
Je l'ai donc tout fonctionne. Je ne sais tout simplement pas comment cela fonctionne exactement! Je veux également pouvoir créer une réponse comme celle-ci, pas seulement à partir de RSpec. :-)
Hier, je me suis vraiment amusé avec cette question pour faire quelque chose de très similaire. En fait, j'ai écrit la question: téléchargement Base64 depuis Android/Java vers RoR Carrierwave
Il en est résulté la création de cet objet image téléchargé dans le contrôleur, puis sa réinjection dans les paramètres.
Pour cet exemple spécifique, nous prenons un fichier base64 (que je suppose que vous avez, car JSON ne prend pas en charge les fichiers incorporés) et l'enregistrons en tant que fichier temporaire dans le système, puis nous créons cet objet UploadedFile et enfin le réinjectons dans le params.
À quoi ressemble mon json/params:
picture {:user_id => "1", :folder_id => 1, etc., :picture_path {:file => "base64 awesomeness", :original_filename => "my file name", :filename => "my file name"}}
Voici à quoi ressemble mon contrôleur maintenant:
# 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
La seule chose qui reste à faire à ce stade est de supprimer le fichier temporaire, ce qui, je pense, peut être fait avec tempfile.delete
J'espère que cela répond à votre question! J'ai passé toute la journée à chercher une solution hier, et tout ce que j'ai vu est une impasse. Cela fonctionne cependant sur mes cas de test.
TomJ a donné une bonne réponse, mais au moins dans Rails 3/Ruby 1.9 il y a quelques trous mineurs.
Tout d'abord, n'essayez pas d'appeler [] sur ce qui pourrait être un objet UploadedFile dans votre objet params. Assurez-vous de vérifier d'abord .is_a?(Hash)
, par exemple.
Assurez-vous également que vous tempfile.rewind()
après avoir écrit, sinon vous obtiendrez des fichiers de longueur 0.
La clé :original_filename
Dans les paramètres du constructeur de UploadedFile est inutile/inutilisée. D'un autre côté, vous souhaiterez peut-être fournir une clé :type
. mime_type = Mime::Type.lookup_by_extension(File.extname(original_filename)[1..-1]).to_s
pour trouver facilement la valeur du type
Voici une version avec les changements appliqué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
fin
Il y a un bijou génial à cet effet si vous utilisez carrierwave