web-dev-qa-db-ja.com

Retrofitでmultipart / form-dataを送信する方法は?

ArticleからandAndroidクライアントにREST=サーバー。サーバーからのPythonモデル:

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

次のインターフェイスは、以前の実装を説明しています。

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

次に、Articleデータを含む画像を送信します。 photoは、AndroidクライアントのArticleモデルの一部ではありません。

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

APIは準備され、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"
_

AndroidクライアントからcreateArticle()を介してデータを送信すると、フィールドが必須/欠落していることを示す_HTTP 400_ステータスを受け取ります

_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
_

これは、サーバー側で_request.data_として受信されるものです。

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

マルチパート適合データ型のArticleオブジェクトを変換するにはどうすればよいですか? Retrofit は、このために Converters を使用できる可能性があることを読みました。 documentation について理解している限り、_retrofit.mime.TypedOutput_を実装するものでなければなりません。

マルチパートパーツはRestAdapterのコンバーターを使用するか、TypedOutputを実装して独自のシリアル化を処理できます。

関連する

29
JJD

カールリクエストによると、次のようなsmthを作成しようとしています。

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

改造アダプタを使用すると、次の方法でこのリクエストを作成できます。

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

使用法:

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(<...>);

同様の出力が作成されます:

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

ここでの主な違いは、POJO Article articleをマルチパートパラメーターとして使用したことです。デフォルトでは、Converterによってjsonに変換されます。そして、サーバーは代わりにプレーンな文字列を期待しています。 curlでは、{"author":"cURL"}ではなくcURLを送信しています。

26

サーバーは「author」文字列を期待していますが、「article」オブジェクトを渡そうとしています。 「Article article」ではなく「String author」に渡します。

また、ファイルが「request.data」に明確に存在するため、「ファイルが送信されていません」というエラーは赤いニシンだと思います。

2
Bob Lee