6

I am uploading a dynamic number of files in a single multipart request using Retrofit2. My retrofit interface looks like this -

public interface FileUploadService {  
    @Multipart
    @POST("upload")
    Call<ResponseBody> uploadMultipleFilesDynamic(
            @Part List<MultipartBody.Part> files);
}

Now I want to track progress for this multi-file upload. This solution explains how to get progress while uploading a single file in a multipart request by extending RequestBody. Though I can't seem to comprehend how to apply this for my multiple files request. One solution I could think of was to create ProgressRequestBody by extending OkHTTP MultipartBody class instead of RequestBody but OkHTTP3 implements MultipartBody as a final class making it impossible to extend. Can anyone point me in the right direction as it's a huge blocker for me to not be able to show the progress to the user for files upload. Or are there any work arounds that I can implement to achieve this functionality?

shubham1g5
  • 407
  • 5
  • 13
  • I need same solution have you found any one ? – Khizar Hayat Jul 20 '17 at 13:52
  • @KhizarHayat You can achieve this by making ProgressRequestBody takes a part number and a progress listener as arguments. ProgressRequestBody can then call the progress listener passing part number as argument and the no of bytes transferred. The listener implementation then will do the combined progress calculation for the multipart request by taking into account the current part number, size of all parts and size of parts transferred till now. – shubham1g5 Jul 23 '17 at 21:06
  • Thanks for reply @shubam1g5 . can you plz share some code ? – Khizar Hayat Jul 24 '17 at 11:10
  • You can check this [Link](https://stackoverflow.com/a/33384551/7609347) – Vivek Barai Jan 11 '18 at 08:14
  • For those still stuck on Retrofit 1.x, here's an easy, working solution that we ended up using: https://stackoverflow.com/a/24772058/293280 – Joshua Pinter Jul 05 '18 at 20:42

1 Answers1

0

I've followed this blogpost: https://medium.com/@PaulinaSadowska/display-progress-of-multipart-request-with-retrofit-and-rxjava-23a4a779e6ba and then made the following adjustments to display the total progression instead of progression of separate files:

  private fun prepareFileParts(reportAttachments: MutableList<ReportAttachment>, emitter: FlowableEmitter<Double>): List<MultipartBody.Part> {
    val multiPartBodyList = mutableListOf<MultipartBody.Part>()

    var offset = 0L
    var totalLength = 0L

    // calculate the total length of all files
    for (attachment in reportAttachments) {
        val file = File(attachment.filePath)
        totalLength += file.length()
    }

    // create requestbody for each file and calculate the progression offset
    for (attachment in reportAttachments) {
        val file = File(attachment.filePath)
        val mimeType = attachment.mimeType

        val countingRequestBody = createCountingRequestBody(file, mimeType, emitter, offset, totalLength)
        offset += file.length()

        val multipartBody = MultipartBody.Part.createFormData("file", file.name, countingRequestBody)
        multiPartBodyList.add(multipartBody)
    }

    return multiPartBodyList
}

private fun createCountingRequestBody(file: File, mimeType: String, emitter: FlowableEmitter<Double>, offset: Long, totalLength: Long): RequestBody {
    val requestBody = RequestBody.create(MediaType.parse(mimeType), file)
    return CountingRequestBody(requestBody, object : CountingRequestBody.Listener {
        override fun onRequestProgress(bytesWritten: Long, contentLength: Long) {
            val progress: Double = 1.0 * (offset + bytesWritten) / totalLength
            emitter.onNext(progress)
        }
    })
}

If you want, you can also create an interceptor and add it to your OkHttpClient. This would track all outgoing API calls by default. It would look something like this:

class UploadProgressInterceptor(private val progressListener: CountingRequestBody.Listener) : Interceptor {

override fun intercept(chain: Interceptor.Chain): Response {
    val originalRequest = chain.request()

    if (originalRequest.body() == null) {
        return chain.proceed(originalRequest)
    }

    val requestBody = originalRequest.body()
    requestBody?.let {
        val progressRequest = originalRequest.newBuilder()
                .method(originalRequest.method(), CountingRequestBody(it, progressListener))
                .build()
        return chain.proceed(progressRequest)
    }
    return chain.proceed(originalRequest)
}
Tim
  • 669
  • 1
  • 8
  • 27