How to POST InputStream as the body of a request in Retrofit?

前端 未结 5 641
刺人心
刺人心 2021-01-18 00:52

I\'m attempting to do a POST with the body being an InputStream with something like this:

@POST(\"/build\")
@Headers(\"Content-Type: application/tar\")
Respo         


        
相关标签:
5条回答
  • 2021-01-18 00:57

    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.

    0 讨论(0)
  • 2021-01-18 00:59

    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

    0 讨论(0)
  • 2021-01-18 01:00

    According to the Multipart section of http://square.github.io/retrofit/ you'll want to use TypedOutput instead of TypedInput. Following their examples for multipart uploads worked fine for me once I had implemented a TypedOutput class.

    0 讨论(0)
  • 2021-01-18 01:07

    My solution was to implement 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);
        }
    }
    
    0 讨论(0)
  • 2021-01-18 01:09

    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.

    0 讨论(0)
提交回复
热议问题