如何在 Retrofit 中将 InputStream 作为请求的主体发布?
Posted
技术标签:
【中文标题】如何在 Retrofit 中将 InputStream 作为请求的主体发布?【英文标题】:How to POST InputStream as the body of a request in Retrofit? 【发布时间】: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 的正确方法是什么?
【问题讨论】:
类似问题:***.com/questions/45123696/… 【参考方案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://***.com/a/61592000/10030693
【讨论】:
【参考方案2】:TypedInput
是 InputStream
的包装器,其中包含用于发出请求的长度和内容类型等元数据。您需要做的就是提供一个实现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);
【讨论】:
以上是关于如何在 Retrofit 中将 InputStream 作为请求的主体发布?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Java 中将 InputStream 转换为字符串?
如何在 Java 中将 InputStream 转换为字符串?
如何在 Ktor 中设置类似于 Retrofit 的`Retrofit.Builder().baseUrl(baseUrl) 的 basePath?