Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature Request] - RequestBody supports InputStream #3585

Closed
jaredsburrows opened this issue Sep 5, 2017 · 9 comments

Comments

@jaredsburrows
Copy link
Contributor

commented Sep 5, 2017

I would like to submit a PR to add support for InputStreams in the RequestBody class. As of now, RequestBody supports the following:

  • create(MediaType contentType, String content)
  • create(MediaType contentType, ByteString content)
  • create(MediaType contentType, byte[] content)
  • create(MediaType contentType, byte[] content, final int offset, final int byteCount)
  • create(MediaType contentType, File file)

I would like to go one step further and add support for InputStream:

  • create(MediaType contentType, Inputream inputStream)

Similar to https://stackoverflow.com/a/25384793:

  public static RequestBody create(final @Nullable MediaType contentType, final InputStream inputStream) {
    if (inputStream == null) throw new NullPointerException("inputStream == null");

    return new RequestBody() {
      @Override public @Nullable MediaType contentType() {
        return contentType;
      }

      @Override public long contentLength() {
        return inputStream.available() == 0 ? -1 : inputStream.available();
      }

      @Override public void writeTo(BufferedSink sink) throws IOException {
        Source source = null;
        try {
          source = Okio.source(inputStream);
          sink.writeAll(source);
        } finally {
          Util.closeQuietly(source);
        }
      }
    };
  }

This would be nice to have since Okio.source support both File and InputStream.

@JakeWharton

This comment has been minimized.

Copy link
Collaborator

commented Sep 5, 2017

@jaredsburrows

This comment has been minimized.

Copy link
Contributor Author

commented Sep 5, 2017

Are RequestBodys written multiple times on retries?

What would you suggest for contentResolver.openInputStream(uri)? Save it to a file first then use create(MediaType contentType, File file)? Even that uses Okio.source when uses a FileInputStream.

@JakeWharton

This comment has been minimized.

Copy link
Collaborator

commented Sep 5, 2017

@swankjesse

This comment has been minimized.

Copy link
Member

commented Sep 5, 2017

Also, request bodies may be written zero times, in which case this would leak the input stream.

@jaredsburrows

This comment has been minimized.

Copy link
Contributor Author

commented Sep 5, 2017

@swankjesse Maybe I could pass a Uri instead of InputStream in the class to prevent leaking using.

@JakeWharton If I call contentResolver.openInputStream(uri) inside of writeTo, how do I update the long contentLength() before the request is made?

@JakeWharton

This comment has been minimized.

Copy link
Collaborator

commented Sep 5, 2017

@jaredsburrows

This comment has been minimized.

Copy link
Contributor Author

commented Sep 5, 2017

@JakeWharton I know it is not an Android library. I figured after the first comment, I would have to keep this code locally and not be able to make a PR. Something like this:

public class InputStreamRequestBody extends RequestBody {
    private final InputStream inputStream;
    private final MediaType contentType;

    public InputStreamRequestBody(MediaType contentType, InputStream inputStream) {
        if (inputStream == null) throw new NullPointerException("inputStream == null");
        this.contentType = contentType;
        this.inputStream = inputStream;
    }

    @Nullable
    @Override
    public MediaType contentType() {
        return contentType;
    }

    @Override
    public long contentLength() throws IOException {
        return inputStream.available() == 0 ? -1 : inputStream.available();
    }

    @Override
    public void writeTo(@NonNull BufferedSink sink) throws IOException {
        Source source = null;
        try {
            source = Okio.source(inputStream);
            sink.writeAll(source);
        } finally {
            Util.closeQuietly(source);
        }
    }
}

Based on your changes, @JakeWharton, to be changed to handle Uri:

public class InputStreamRequestBody extends RequestBody {
    private final MediaType contentType;
    private final ContentResolver contentResolver;
    private final Uri uri;

    public InputStreamRequestBody(MediaType contentType, ContentResolver contentResolver, Uri uri) {
        if (uri == null) throw new NullPointerException("uri == null");
        this.contentType = contentType;
        this.contentResolver = contentResolver;
        this.uri = uri;
    }

    @Nullable
    @Override
    public MediaType contentType() {
        return contentType;
    }

    @Override
    public long contentLength() throws IOException {
        return -1;
    }

    @Override
    public void writeTo(@NonNull BufferedSink sink) throws IOException {
        sink.writeAll(Okio.source(contentResolver.openInputStream(uri)));
    }
}
@JakeWharton

This comment has been minimized.

Copy link
Collaborator

commented Sep 5, 2017

@jaredsburrows

This comment has been minimized.

Copy link
Contributor Author

commented Sep 6, 2017

@JakeWharton Based on your first comment, I guess I will close this because using TypeInputStream directly does not work very well with the current implementation of RequestBody.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
3 participants
You can’t perform that action at this time.