Comment envoyer des données multipart / form avec Retrofit?

je veux envoyer un Article de et Android client à un serveur de repos. Voici le modèle Python du serveur:

class Article(models.Model):
    author = models.CharField(max_length=256, blank=False)
    photo = models.ImageField()

l'interface suivante décrit l'ancienne implémentation:

@POST("/api/v1/articles/")
public Observable<CreateArticleResponse> createArticle(
        @Body Article article
);

maintenant je veux envoyer une image avec les données Article . Le photo ne fait pas partie de l'Article modèle sur le client Android .

@Multipart
@POST("/api/v1/articles/")
public Observable<CreateArticleResponse> createArticle(
        @Part("article") Article article,
        @Part("photo") TypedFile photo
);

l'API est préparée et testée avec succès avec cURL .

$ curl -vX POST http://localhost:8000/api/v1/articles/ 
    -H "Content-Type: multipart/form-data" 
    -H "Accept:application/json" 
    -F "author=cURL" 
    -F "photo=@/home/user/Desktop/article-photo.png"

lorsque j'envoie des données via createArticle() à partir du Android client je reçois un statut HTTP 400 indiquant que les champs sont requis/manquants .

D  <--- HTTP 400 http://192.168.1.1/articles/ (2670ms)
D  Date: Mon, 20 Apr 2015 12:00:00 GMT
D  Server: WSGIServer/0.1 Python/2.7.8
D  Vary: Accept, Cookie
D  X-Frame-Options: SAMEORIGIN
D  Content-Type: application/json
D  Allow: GET, POST, HEAD, OPTIONS
D  OkHttp-Selected-Protocol: http/1.0
D  OkHttp-Sent-Millis: 1429545450469
D  OkHttp-Received-Millis: 1429545453120
D  {"author":["This field is required."],"photo":["No file was submitted."]}
D  <--- END HTTP (166-byte body)
E  400 BAD REQUEST

C'est ce que est reçu comme request.data du côté du serveur:

ipdb> print request.data  
  <QueryDict: {u'article': [u'{"author":"me"}'], 
  u'photo': [<TemporaryUploadedFile: IMG_1759215522.jpg 
  (multipart/form-data)>]}>

comment convertir l'objet Article dans un type de données conform multipart? J'ai lu que Rénovation pourrait permettre d'utiliser les Convertisseurs pour cela. Il devrait être quelque chose qui met en œuvre un retrofit.mime.TypedOutput autant que j'ai compris pour le documentation .

pièces multiparties utiliser le RestAdapter de l ' convertisseur ou ils peuvent mettre en œuvre TypedOutput pour gérer leur propre sérialisation.

lié

  • HTML 4.01 Spécification-forme soumission-multipart / form-data
  • documentation relative à la pièce de type adaptation
  • Télécharger " multipart des données d'image JSON avec rénovation?
  • REST-HTTP Post Multipart with JSON
  • Rénovation Multipart Télécharger l'Image a échoué
  • Rénovation question n ° 178: Créer un manuel pour l'envoi de fichiers avec rénovation
  • Rénovation question n ° 531: Problème de téléchargement de fichier via la POSTE/Multipart
  • Retrofit issue # 658: impossible d'envoyer des paramètres de chaîne avec l'image en utilisant Multipart
  • Rénovation question n ° 662: Rénovation Forme Codée et en plusieurs parties dans une seule demande
26
demandé sur Community 2015-04-16 19:01:30

2 réponses

selon votre requête curl vous essayez de créer smth comme ceci:

POST http://localhost:8000/api/v1/articles/ HTTP/1.1
User-Agent: curl/7.30.0
Host: localhost
Connection: Keep-Alive
Accept: application/json
Content-Length: 183431
Expect: 100-continue
Content-Type: multipart/form-data; boundary=----------------------------23473c7acabb

------------------------------23473c7acabb
Content-Disposition: form-data; name="author"

cURL
------------------------------23473c7acabb
Content-Disposition: form-data; name="photo"; filename="article-photo.png"
Content-Type: application/octet-stream

‰PNG

<!RAW BYTES HERE!>

M\UUÕ+4qUUU¯°WUUU¿×ß¿þ Naa…k¿    IEND®B`‚
------------------------------23473c7acabb--

avec adaptateur d'adaptation cette requête peut être créée de la manière suivante:

@Multipart
@POST("/api/v1/articles/")
Observable<Response> uploadFile(@Part("author") TypedString authorString,
                                @Part("photo") TypedFile photoFile);

Utilisation:

TypedString author = new TypedString("cURL");
File photoFile = new File("/home/user/Desktop/article-photo.png");
TypedFile photoTypedFile = new TypedFile("image/*", photoFile);
retrofitAdapter.uploadFile(author, photoTypedFile)
               .subscribe(<...>);

qui crée une sortie similaire:

POST http://localhost:8000/api/v1/articles/ HTTP/1.1
Content-Type: multipart/form-data; boundary=32230279-83af-4480-abfc-88a880b21b19
Content-Length: 709
Host: localhost
Connection: Keep-Alive
Accept-Encoding: gzip
User-Agent: okhttp/2.3.0

--32230279-83af-4480-abfc-88a880b21b19
Content-Disposition: form-data; name="author"
Content-Type: text/plain; charset=UTF-8
Content-Length: 4
Content-Transfer-Encoding: binary

cUrl
--32230279-83af-4480-abfc-88a880b21b19
Content-Disposition: form-data; name="photo"; filename="article-photo.png"
Content-Type: image/*
Content-Length: 254
Content-Transfer-Encoding: binary

<!RAW BYTES HERE!>

--32230279-83af-4480-abfc-88a880b21b19--

la différence clé ici est que vous avez utilisé POJO Article article comme multipart param, qui par défaut est converti par Converter en json. Et votre serveur attend de la chaîne simple à la place. Avec curl vous envoyez cURL , pas {"author":"cURL"} .

25
répondu Sergii Pechenizkyi 2015-04-21 15:31:00

le serveur attend une chaîne" auteur "mais vous essayez de lui passer un objet" article". Passez-le " String author "au lieu de" Article article."

aussi, je pense que l'erreur" no file submitted "est une diversion parce que le fichier est clairement présent dans votre" requête.données."

2
répondu Bob Lee 2015-04-21 15:10:30