【问题标题】:Multipart Request using Retrofit 1.8.0 not working使用 Retrofit 1.8.0 的多部分请求不起作用
【发布时间】:2014-03-02 04:35:18
【问题描述】:

我有 4 天的时间,尝试在 android 中使用 Retrofit 1.8.0 发出多部分请求并取得任何成功。我的界面看起来像这样

@Multipart
@POST("/posts/add.json") 
void addComment(
  @Part("id") String id,
  @Part("post[body]") String body,
  @Part("post[attachment]") TypedFile attachment,
  Callback<Map<String, String>> callback );

但是,在服务器端,我收到以下内容

Parameters: {"id"=># <File:/var/folders/z0/0ggjvvfj4t1fdsvbxf3lc9pw0000gn/T/RackMultipart9853-0>, "post"=>{"body"=>#<File:/var/folders/z0/0ggjvvfj4t1fdsvbxf3lc9pw0000gn/T/RackMultipart9853-1>, "attachment"=>#<File:/var/folders/z0/0ggjvvfj4t1fdsvbxf3lc9pw0000gn/T/RackMultipart9853-2>}, "controller"=>"posts", "action"=>"add", "format"=>"json"}

如您所见,文件部分在每个部分都发送它,但我缺少 id 和 post[body] 的参数值

这就是 Retrofit 试图发送的内容

 02-06 15:01:16.213    32545-822/com.myapp D/Retrofit﹕ --fe41634b-6826-4ee4-95cb-65efb0ca66c2
Content-Disposition: form-data; name="id"
Content-Type: text/plain; charset=UTF-8
Content-Length: 3
Content-Transfer-Encoding: binary
189
--fe41634b-6826-4ee4-95cb-65efb0ca66c2
Content-Disposition: form-data; name="post[body]"
Content-Type: text/plain; charset=UTF-8
Content-Length: 4
Content-Transfer-Encoding: binary
test
--fe41634b-6826-4ee4-95cb-65efb0ca66c2
Content-Disposition: form-data; name="post[attachment]"; filename="IMG_20140203_144358.jpg"
Content-Type: image/jpg
Content-Length: 1615460
Content-Transfer-Encoding: binary
����/�Exif����MM��*���������

这是 HttpMime 库在 Multipart 中发送的内容,不同之处在于针对 Retrofit 的“Content-Transfer-Encoding”标头

Content-Disposition: form-data; name="id"
Content-Type: text/plain; charset=US-ASCII
Content-Transfer-Encoding: 8bit

Content-Disposition: form-data; name=“post[body]"
Content-Type: text/plain; charset=US-ASCII
Content-Transfer-Encoding: 8bit

Content-Disposition: form-data; name=“post[attachment]"; filename="images.jpg"
Content-Type: image/jpg
Content-Transfer-Encoding: binary

有什么线索吗?提前致谢

------------------解决方案------------ ----------------------

最后,我以这种方式解决了,实际上我的答案非常接近@lazypig,这是一个很好的指导方针

我唯一改变的是他的类“ByteArrayTypedOutput”

我创建了一个名为“MultipartTypedOutputCustom”的类http://pastie.org/10549360

这就是我的界面现在的样子

“PostsRetrofitAPI.java”类

@POST("/posts/add.json")
    void addComment(@Body MultipartTypedOutputCustom parts,
                    Callback<Map<String, String>> callback);

“PostsService.java”类

//Properties
private PostsRetrofitAPI mApi;
...

    @Override
        public void addComment(ServiceResponseHandler<Map<String, String>> handler, String id, String body, TypedFile attachment) {
           MultipartTypedOutputCustom parts = new MultipartTypedOutputCustom();
           parts.addPart("id", new TypedString(id));
           parts.addPart("post[body]", new TypedString(body));
           parts.addPart("post[attachment]", attachment);
    objectRetrofitCallback= new ObjectRetrofitCallback(handler, ServerError.class, ClientError.class);
            mApi.addComment(parts, objectRetrofitCallback);
        }

【问题讨论】:

  • 你能发布你的 HTTP 信封吗?在 RestAdapter.Builder().setLogLevel(LogLevel.FULL) 上启用日志
  • 嗨,我已经根据你的建议编辑了我的帖子
  • 你最后搞清楚了吗?
  • 这是什么结论,我在我的座位边缘......

标签: android multipart retrofit


【解决方案1】:

如果您看到http://square.github.io/retrofit/ 上的示例,则“id”和“part[body]”参数的对象类型需要是 TypedString 而不是 String。 TypedString 设置适当的 MIME 类型并转换为字节:

https://github.com/square/retrofit/blob/master/retrofit/src/main/java/retrofit/mime/TypedString.java

【讨论】:

    【解决方案2】:

    我这样做是为了界面

    interface MultipartFormDataService {
        @POST("/{uploadPath}")
        void multipartFormDataSend(
                @EncodedPath("uploadPath") String uploadPath,
                @Body MultipartTypedOutput multipartTypedOutput,
                Callback<String> cb);
    }
    

    然后当我调用它时,它看起来像这样

    // creating the Multipart body using retrofit
    MultipartTypedOutput multipartTypedOutput = new MultipartTypedOutput();
    TypedString idParam = new TypedString("[ID Value]")
    TypedString bodyParam = new TypedString("[Body text]")
    ByteArrayTypedOutput byteMultipartTypedOut = new ByteArrayTypedOutput(bytes)
    
    // add parts
    multipartTypedOutput.addPart("id", idParam);
    multipartTypedOutput.addPart("body", bodyParam);
    multipartTypedOutput.addPart("attachment", extraParamTypedString);
    
    // send
    multipartService.multipartFormDataSend(
                    "[TARGET URL]",
                    multipartTypedOutput,
                aCallback);
    

    我的 ByteArrayTypedOutput 很简单

    public class ByteArrayTypedOutput implements TypedOutput {
    
        private MultipartFormMetadata metadata;
        private byte[] imageData;
    
        public ByteArrayTypedOutput(MultipartFormMetadata metadata, byte[] imageData)
            this.metadata = metadata;
            this.imageData = imageData;
        }
    
        @Override
        public String fileName() {
            return metadata.fileName;
        }
    
        @Override
        public String mimeType() {
            return metadata.fileMimeType;
        }
    
        @Override
        public long length() {
            return imageData.length;
        }
    
        @Override
        public void writeTo(OutputStream outputStream) throws IOException {
             outputStream.write(imageData);
        }
    }
    

    【讨论】:

      【解决方案3】:

      我今天发送文件和一些字段时遇到了类似的问题,这是我的解决方案

      我的界面,TypedFile 是一个 Retrofit 类

          @Multipart
          @POST("/api/Media/add")
          void addMedia(@Part("file") TypedFile photo,
                        @Part("type") String type,
                        @Part("name") String name,
                        @Part("description") String description,
                        Callback<com.yourcompany.pojo.Media> callback);
      

      在活动中

      profileImage.setOnClickListener(new View.OnClickListener() {
              @Override
              public void onClick(View v) {
                  Intent galleryIntent = new Intent(
                          Intent.ACTION_PICK,
                          MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
                  startActivityForResult(galleryIntent , RESULT_GALLERY );
              }
          });
      

      然后

      @Override
      public void onActivityResult(int requestCode, int resultCode, Intent data) {
          super.onActivityResult(requestCode, resultCode, data);
      
          switch (requestCode) {
              case RESULT_GALLERY :
                  if (null != data) {
                      imageUri = data.getData();
                      String selectedImagePath = null;
                      Uri selectedImageUri = data.getData();
                      Cursor cursor = activity.getContentResolver().query(selectedImageUri, null, null,
                      null, null);
                      if (cursor == null) {
                          selectedImagePath = imageUri.getPath();
                      } else {
                          cursor.moveToFirst();
                          int idx = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
                          selectedImagePath = cursor.getString(idx);
                      }
                      File file = new File(selectedImagePath);
                      waiter.setVisibility(View.VISIBLE);
                      _YOUR_APP_._YOUR_INTERFACE_.addMedia(new TypedFile("image/*", file), "avatar", "avatar", "avatar", new Callback<Media>() {
      
                          @Override
                          public void success(Media media, Response response) {
      
                          }
      
                          @Override
                          public void failure(RetrofitError error) {
      
                          }
                      });
                  }
                  break;
              default:
                  break;
          }
      }
      

      媒体类是保持答案的简单pojo

      public class Media {
      
      @Expose
      private int[] data;
      
      /**
       *
       * @return
       * The data
       */
      public int getData() {
          return data[0];
      }
      
      /**
       *
       * @param data
       * The data
       */
      public void setData(int[] data) {
          this.data = data;
      }
      
      }
      

      【讨论】:

        猜你喜欢
        • 2013-08-08
        • 1970-01-01
        • 2021-04-01
        • 1970-01-01
        • 2020-04-12
        • 2015-02-22
        • 2021-04-20
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多