4

Has anyone been able to accomplish sending a multipart/form-data POST in Android with CRONET yet? I have had no success trying to upload an image/png using a POST request to our server and am curious if anyone has.

notes : i need CRONET based solution only

Post Method Example:

val myBuilder = CronetEngine.Builder(context)
// Enable caching of HTTP data and
// other information like QUIC server information, HTTP/2 protocol and QUIC protocol.
val cronetEngine: CronetEngine = myBuilder
    .enableHttpCache(CronetEngine.Builder.HTTP_CACHE_IN_MEMORY, 100 * 1024.toLong())
    .enableHttp2(true)
    .enableQuic(true)
    .build()
val executor: Executor = Executors.newSingleThreadExecutor()
val requestBuilder = cronetEngine.newUrlRequestBuilder(
    "FULL-URL",
    MyUrlRequestCallback(),
    executor
)
// Content-Type is required, removing it will cause Exception
requestBuilder.addHeader("Content-Type","application/json; charset=UTF-8")
requestBuilder.setHttpMethod("POST")
val myUploadDataProvider = MyUploadDataProvider()
requestBuilder.setUploadDataProvider(myUploadDataProvider,executor)
val request: UrlRequest = requestBuilder.build()
request.start()

MyUploadDataProvider Class:

import android.util.Log
import org.chromium.net.UploadDataProvider
import org.chromium.net.UploadDataSink
import java.lang.Exception
import java.nio.ByteBuffer
import java.nio.charset.StandardCharsets

private const val TAG = "MyUploadDataProvider"
//TODO replace username and passowrd "_user & _pass"
var string: String ="{\"username\":\"_user\",\"password\":\"_pass\"}"
val charset = StandardCharsets.UTF_8

class MyUploadDataProvider() : UploadDataProvider() {

    override fun getLength(): Long {
    val size:Long = string.length.toLong()
    Log.e(TAG,"Length = "+size)
    return size
    }

    override fun rewind(uploadDataSink: UploadDataSink?) {
    Log.e(TAG,"REWIND IS CALLED")
    uploadDataSink!!.onRewindSucceeded()
    }

    override fun read(uploadDataSink: UploadDataSink?, byteBuffer: ByteBuffer?) {
    Log.e(TAG,"READ IS CALLED")
    byteBuffer!!.put(string.toByteArray(charset))
    //byteBuffer.rewind()
    //For chunked uploads, true if this is the final read. It must be false for non-chunked uploads.
    uploadDataSink!!.onReadSucceeded(false)
    }

}

2 Answers2

0

To upload a file, I would rather use create() methods from UploadDataProviders. With help of these methods, you can create an UploadDataProvider for uploading a File, FileDescriptor or even a byte array. For instance, to upload an empty array of bytes, I could do something as:

...
val myUploadDataProvider = UploadDataProviders.create(byteArrayOf())
...

Or, If I have a File object, I can also pass it directly into the create method:

...
val myUploadDataProvider = UploadDataProviders.create(someFile)
...

So, the whole code can look as follows:

...
    val myBuilder = CronetEngine.Builder(this)
    val cronetEngine: CronetEngine = myBuilder
            .enableHttpCache(CronetEngine.Builder.HTTP_CACHE_IN_MEMORY, 100 * 1024.toLong())
            .enableHttp2(true)
            .enableQuic(true)
            .build()
    val executor: Executor = Executors.newSingleThreadExecutor()
    val requestBuilder = cronetEngine.newUrlRequestBuilder(
            "http://ptsv2.com/",
            object : UrlRequest.Callback() {
                override fun onResponseStarted(request: UrlRequest?, info: UrlResponseInfo?) {
                }

                override fun onReadCompleted(request: UrlRequest?, info: UrlResponseInfo?, byteBuffer: ByteBuffer?) {

                }

                override fun onFailed(request: UrlRequest?, info: UrlResponseInfo?, error: CronetException?) {
                }

                override fun onSucceeded(request: UrlRequest?, info: UrlResponseInfo?) {
                }

                override fun onRedirectReceived(request: UrlRequest?, info: UrlResponseInfo?, newLocationUrl: String?) {

                }

            },
            executor
    )
    requestBuilder.addHeader("Content-Type","application/json; charset=UTF-8")
    requestBuilder.setHttpMethod("POST")
    val myUploadDataProvider = UploadDataProviders.create(byteArrayOf())
    requestBuilder.setUploadDataProvider(myUploadDataProvider,executor)
    val request: UrlRequest = requestBuilder.build()
    request.start()
...
Anatolii
  • 14,139
  • 4
  • 35
  • 65
0

yes, cronet and volley is google officially supported library but please also consider the other side of google...google also official support retrofit...because its fast and less boilerplate code...please read some blogs and also you can visit udacity nano degree course where say it retrofit is best for networking...

axar
  • 539
  • 2
  • 17
  • so far i am using volley lib is good, sometime volley post method hit twice time in slow internet (tried many handling like retries policy - none solved my issue) but CRONET is only solved my issue –  Apr 13 '21 at 04:39
  • The Cronet Library handles the requests of apps used by millions of people on a daily basis, such as YouTube, Google App, Google Photos, and Maps - Navigation & Transit. (https://developer.android.com/guide/topics/connectivity/cronet) –  Apr 13 '21 at 04:39