2015-04-16 22 views
26

Tôi muốn gửi Điều từ và Android ứng dụng khách đến máy chủ REST. Đây là Python mô hình từ máy chủ:Làm cách nào để gửi nhiều dữ liệu/biểu mẫu với Retrofit?

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

Giao diện sau đây mô tả việc thực hiện trước đây:

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

Bây giờ tôi muốn gửi một hình ảnh với Điều dữ liệu. photo không phải là một phần của mô hình Điều trên máy khách Android của khách hàng.

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

API được chuẩn bị và thử nghiệm thành công với cURL.

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

Khi tôi gửi dữ liệu thông qua createArticle() từ Android client tôi nhận được một tình trạng HTTP 400 nói rằng các lĩnh vực được yêu cầu/thiếu.

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 

Đây là những gì được nhận như request.data ở phía máy chủ:

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

Làm thế nào có thể chuyển đổi các Điều đối tượng trong một nhiều phần dữ liệu phù hợp với kiểu dữ liệu? Tôi đọc rằng Retrofit có thể cho phép sử dụng Converters cho việc này. Nó phải là một cái gì đó mà thực hiện một retrofit.mime.TypedOutput theo như tôi hiểu cho documentation.

Bộ phận đa phần sử dụng bộ chuyển đổi RestAdapter hoặc có thể triển khai TypedOutput để xử lý tuần tự của riêng chúng.

liên quan

+2

Đây không phải là lớp TypedFile có thể được sử dụng cho điều này? – user2511882

+0

Phương pháp của bạn có vẻ ổn. Tại sao bạn không cho phép đăng nhập vào 'RestAdapter' và kiểm tra chính xác dữ liệu nào đang được gửi đi. – corsair992

+0

@ user2511882 Bạn có nghĩa là tôi nên sử dụng 'TypedFile' cho cả dữ liệu JSON (' bài viết') và hình ảnh? Vui lòng chỉ cho tôi cách chuyển đổi dữ liệu./@ corsair992 Tôi đã cập nhật bài đăng của mình. – JJD

Trả lời

24

Theo yêu cầu curl của bạn, bạn đang cố gắng để tạo ra smth như thế này:

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

Với bộ chuyển đổi trang bị thêm yêu cầu này có thể được tạo ra theo cách tiếp :

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

Cách sử dụng:

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

nào tạo ra tương tự:

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

Sự khác biệt chính ở đây là bạn sử dụng POJO Article article như param nhiều phần dữ liệu, mà theo mặc định được chuyển đổi bởi Converter vào json. Và máy chủ của bạn mong muốn chuỗi đơn giản thay thế. Với curl bạn đang gửi cURL, không phải {"author":"cURL"}.

+0

Có thể chuyển đổi toàn bộ mô hình * Điều * thành một 'Kiểu chữ 'cùng một lúc và chuyển nó vào API thay vì kéo ra từng thành viên? Có lẽ một công cụ chuyển đổi có thể làm điều này? – JJD

+1

Có, tuyệt đối. Tôi đã không nhìn thấy công cụ chuyển đổi như vậy, bạn có thể muốn viết nó và điều chỉnh theo nhu cầu của bạn. Hướng mẫu: https://gist.github.com/plastiv/08538a095d2d35acab05. –

+0

Để tham khảo: Tôi đã đề xuất [MultipartConverter như một tính năng cho Retrofit] (https: // github.com/square/retrofit/issues/819). – JJD

2

Máy chủ dự kiến ​​chuỗi "tác giả" nhưng bạn đang cố truyền cho nó một đối tượng "bài viết". Vượt qua nó "String author" thay vì "Article article".

Ngoài ra, tôi cho rằng lỗi "không có tệp được gửi" là một trích dẫn màu đỏ vì tệp có mặt rõ ràng trong "request.data" của bạn.

Các vấn đề liên quan