如何使用Retrofit发送multipart / form-data?

我想从Android客户端发送文章到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 ); 

现在我想发送一个图像的文章数据。 该photo不是Android客户端上的Article模型的一部分。

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

API已经准备好,并且已经成功通过cURLtesting。

 $ 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对象的多部分符合数据types? 我读了Retrofit可能允许使用转换器 。 就我所知的文档而言 ,它应该是实现retrofit.mime.TypedOutput东西。

多部分使用RestAdapter的转换器,或者他们可以实现TypedOutput处理自己的序列化。

有关

  • HTML 4.01规范 – 表单提交 – multipart / form-data
  • 改造注释types零件文档
  • 使用Retrofit以JSON上传多部分图像数据?
  • REST – 使用JSON的HTTP Post Multipart
  • Retrofit分段上传图像失败
  • 改造问题#178:创build手动发送文件与改造
  • Retrofit问题#531:通过POST / Multipart上传文件时出现问题
  • 改造问题#658:使用Multipart时无法使用图像发送string参数
  • 改造问题#662:在单个请求中改造表单编码和多部分

Solutions Collecting From Web of "如何使用Retrofit发送multipart / form-data?"

根据你的卷发要求,你试图创造这样的水平:

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

使用改造适配器,可以通过下面的方式创build这个请求:

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

其中创build类似的输出:

 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作为多部分参数,它默认被转换成json。 而你的服务器期望纯string。 用curl发送cURL ,而不是{"author":"cURL"}

服务器期望一个“作者”string,但是你想传递一个“文章”对象。 通过“string作者”而不是“条款文章”。

另外,我认为“没有文件提交”的错误是一个红色的鲱鱼,因为该文件清楚地出现在您的“request.data”。