How to POST InputStream as the body of a request in Retrofit?
TypedInput
is a wrapper around an InputStream
that has metadata such as length and content type which is used in making the request. All you need to do is provide a class that implements TypedInput
which passed your input stream.
class TarFileInput implements TypedInput {
@Override public InputStream in() {
return /*your input stream here*/;
}
// other methods...
}
Be sure you pass the appropriate return values for length()
and mimeType()
based on the type of file from which you are streaming content.
You can also optionally pass it as an anonymous implementation when you are calling your build
method.
You can upload inputStream using Multipart
.
@Multipart
@POST("pictures")
suspend fun uploadPicture(
@Part part: MultipartBody.Part
): NetworkPicture
Then in perhaps your repository class:
suspend fun upload(inputStream: InputStream) {
val part = MultipartBody.Part.createFormData(
"pic", "myPic", RequestBody.create(
MediaType.parse("image/*"),
inputStream.readBytes()
)
)
uploadPicture(part)
}
If you want to find out how to get an image Uri, check this answer: https://stackoverflow.com/a/61592000/10030693
The only solution I came up with here was to use the TypeFile class:
TypedFile tarTypeFile = new TypedFile("application/tar", myFile);
and the interface (without explicitly setting the Content-Type header this time):
@POST("/build")
Response build(@Query("t") String tag,
@Query("q") boolean quiet,
@Query("nocache") boolean nocache,
@Body TypedInput inputStream);
Using my own implementation of TypedInput resulted in a vague EOF exception even while I provided the 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);
}
}
Also, while troubleshooting this issue I tried using the latest Apache Http client instead of OkHttp which resulted in a "Content-Length header already present" error even though I wasn't explicitly setting that header.