【问题标题】:How to POST InputStream as the body of a request in Retrofit?如何在 Retrofit 中将 InputStream 作为请求的主体发布?
【发布时间】:2014-04-27 20:16:21
【问题描述】:

我正在尝试将正文作为 InputStream 进行 POST,其内容如下:

@POST("/build")
@Headers("Content-Type: application/tar")
Response build(@Query("t") String tag,
               @Query("q") boolean quiet,
               @Query("nocache") boolean nocache,
               @Body TypedInput inputStream);

在这种情况下,InputStream 来自压缩的 tar 文件。

POST InputStream 的正确方法是什么?

【问题讨论】:

标签: rest docker retrofit


【解决方案1】:

您可以使用Multipart 上传 inputStream。

@Multipart
@POST("pictures")
suspend fun uploadPicture(
        @Part part: MultipartBody.Part
): NetworkPicture

然后在你的存储库类中:

suspend fun upload(inputStream: InputStream) {
   val part = MultipartBody.Part.createFormData(
         "pic", "myPic", RequestBody.create(
              MediaType.parse("image/*"),
              inputStream.readBytes()
          )
   )
   uploadPicture(part)
}

如果您想了解如何获取图像 Uri,请查看此答案:https://stackoverflow.com/a/61592000/10030693

【讨论】:

    【解决方案2】:

    TypedInputInputStream 的包装器,其中包含用于发出请求的长度和内容类型等元数据。您需要做的就是提供一个实现TypedInput 的类,它传递了您的输入流。

    class TarFileInput implements TypedInput {
      @Override public InputStream in() {
        return /*your input stream here*/;
      }
    
      // other methods...
    }
    

    确保根据您从中流式传输内容的文件类型为length()mimeType() 传递适当的返回值。

    当您调用 build 方法时,您还可以选择将其作为匿名实现传递。

    【讨论】:

    • 感谢您的回复,但我在使用 TypedInput 时遇到了问题。我在下面记录了我的解决方案以供将来参考。
    • TypedInput 好像已经不可用了,那么 inputStream 的解决方法是什么?
    • @gropapa 的问题我迟到了一年,但对于其他想知道同样事情的人(比如我):将InputStream 转换为byte[],然后可以用来为 Retrofit 2.x 中的文件上传构造一个 RequestBody。详情请见this link
    【解决方案3】:

    我在这里想出的唯一解决方案是使用 TypeFile 类:

    TypedFile tarTypeFile = new TypedFile("application/tar", myFile);
    

    和接口(这次没有显式设置 Content-Type 标头):

    @POST("/build")
    Response build(@Query("t") String tag,
                   @Query("q") boolean quiet,
                   @Query("nocache") boolean nocache,
                   @Body TypedInput inputStream);
    

    使用我自己的 TypedInput 实现会导致一个模糊的 EOF 异常,即使我提供了 length()。

    public class TarArchive implements TypedInput {
    
        private File file;
    
        public TarArchive(File file) {
            this.file = file;
        }
    
        public String mimeType() {
            return "application/tar";
        }
    
        public long length() {
            return this.file.length();
        }
    
        public InputStream in() throws IOException {
            return new FileInputStream(this.file);
        }
    }
    

    此外,在解决此问题时,我尝试使用最新的 Apache Http 客户端而不是 OkHttp,这导致“Content-Length 标头已存在”错误,即使我没有明确设置该标头。

    【讨论】:

    • 您的TypedInput 实现不适用于文件上传,因为该用例需要一个TypedOutput,正如@BertHartmann's answer 中所指出的那样。不过,错误消息可能很不清楚。
    【解决方案4】:

    根据http://square.github.io/retrofit/ 的Multipart 部分,您需要使用TypedOutput 而不是TypedInput。一旦我实现了 TypedOutput 类,按照他们的分段上传示例对我来说效果很好。

    【讨论】:

      【解决方案5】:

      我的解决方案是实现TypedOutput

      public class TypedStream implements TypedOutput{
      
          private Uri uri;
      
          public TypedStream(Uri uri){
              this.uri = uri;
          }
      
          @Override
          public String fileName() {
              return null;
          }
      
          @Override
          public String mimeType() {
              return getContentResolver().getType(uri);
          }
      
          @Override
          public long length() {
              return -1;
          }
      
          @Override
          public void writeTo(OutputStream out) throws IOException {
              Utils.copyStream(getContentResolver().openInputStream(uri), out);
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-02-27
        • 1970-01-01
        • 2018-01-20
        • 1970-01-01
        • 2014-12-10
        • 1970-01-01
        相关资源
        最近更新 更多