I'm using googles/androids <modern storage > libra...
# squarelibraries
c
I'm using googles/androids modern storage library for choosing a photo. Now I need to upload via okhttp as a PUT request with Binary Data. I created my own InputStreamRequestBody and my solution loosely based off of this post, but my upload seems to fail because my server doesn't like the
Transfer-Encoding: chunked
header. If I replay that request, omitting that header via charles, then the upload succeeds. If I try to remove the header programatically, it still appears in the request (according to charles). Any idea whats up with that? Am I taking the wrong path here?
Copy code
val resolver = getApplication<Application>().contentResolver
val doc = DocumentFile.fromSingleUri(getApplication(), content)!!
val type = (doc.type ?: DEFAULT_TYPE).toMediaType()
val contentPart = InputStreamRequestBody(type, resolver, content)

val request = Request.Builder().url(url).addHeader("DOES", "THIS WORK").put(contentPart).removeHeader("Transfer-Encoding").build()
y
Is the request over http1.1 or 2?
c
Looks like the ios team (which I'm trying to mimic) is using HTTP/1.1 as per charles proxy
y
The reason I asked is that chunked encoding header doesn't work on HTTP/2 IIRC. If using HTTP/1.1, then OkHttp will always put it there based on the Request body. You'd have to remove it via a network interceptor I suspect (but haven't confirmed).
The bridge interceptor (after application interceptors and before network interceptors) adds it.
Copy code
val contentLength = body.contentLength()
      if (contentLength != -1L) {
        requestBuilder.header("Content-Length", contentLength.toString())
        requestBuilder.removeHeader("Transfer-Encoding")
      } else {
        requestBuilder.header("Transfer-Encoding", "chunked")
        requestBuilder.removeHeader("Content-Length")
      }
e
if your request body is a File or Bytestring or something else with a definite length, it won't have the TE:chunked header either
if you created a RequestBody wrapper around AssetFileDescriptor, that should be able to provide a real content-length as well, which would also avoid chunked
c
Thanks everyone. it looks like this is mostly working. Charles says "Binary Image" instead of "Binary Data" (like what iOS says) but the upload seems to work.
Copy code
private val TAG = "MultipartUpload"
private val DEFAULT_TYPE = "application/octet-stream"

private suspend fun uploadMultipart(content: Uri, url: String, fileSize: Long) =
  withContext(<http://Dispatchers.IO|Dispatchers.IO>) {
    val resolver = getApplication<Application>().contentResolver
    val doc = DocumentFile.fromSingleUri(getApplication(), content)!!
    val type = (doc.type ?: DEFAULT_TYPE).toMediaType()
    val contentPart = InputStreamRequestBody(type, resolver, content, fileSize)

    val request = Request.Builder().url(url).put(contentPart)
      .build()

    ok.newCall(request).execute().use { response ->
      if (!response.isSuccessful) throw IOException("Unexpected code $response")
      // success
    }
  }
Copy code
class InputStreamRequestBody(
  private val contentType: MediaType,
  private val contentResolver: ContentResolver,
  private val uri: Uri,
  private val fileSize: Long
) : RequestBody() {
  override fun contentType() = contentType

  override fun contentLength(): Long = fileSize

  @Throws(IOException::class)
  override fun writeTo(sink: BufferedSink) {
    val input = contentResolver.openInputStream(uri)

    input?.use { sink.writeAll(it.source()) }
      ?: throw IOException("Could not open $uri")
  }
}
I honestly have no idea what I'm doing with sinks and sources and stuff so I'd appreciate a once over to make sure I'm doing anything horribly wrong.... but IT WORKS!