如何在Retrofit中把输入流作为请求的主体进行POST?

问题描述 投票:5回答:5

我试图用类似于这样的方式来做一个POST,主体是一个InputStream。

@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输入流的方式是什么?

rest docker retrofit
5个回答
2
投票

TypedInput 是一个包装器,围绕着一个 InputStream 的元数据,如长度和内容类型,用于提出请求。你所需要做的就是提供一个实现了 TypedInput 传递你的输入流。

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

  // other methods...
}

请确保你为 length()mimeType() 基于你流媒体内容的文件类型。

你也可以在调用你的 build 方法。


2
投票

我在这里想到的唯一解决方案是使用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 header already present "的错误,即使我没有明确设置该头。


2
投票

根据多部分的 http:/square.github.ioretrofit。 你会希望使用TypedOutput而不是TypedInput。当我实现了一个TypedOutput类后,按照他们的例子进行多部分上传对我来说很好。


2
投票

我的解决方案是实现 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);
    }
}

1
投票

您可以使用 Multipart.

@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)
}
© www.soinside.com 2019 - 2024. All rights reserved.