Last active
March 29, 2022 07:19
-
-
Save marcRDZ/c62f0fa0246d4f5c4aee3226bab328a3 to your computer and use it in GitHub Desktop.
A bunch of helpful classes based on OkHttp and Okio libraries to provide a progress feedback on download/upload files. In the case of upload you can attach this RequestBody as is, for the downloads you need to use the ResponseBody through an Interceptor also included.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import okhttp3.Interceptor | |
import okhttp3.RequestBody | |
import okhttp3.Response | |
import okhttp3.ResponseBody | |
import okio.* | |
import java.io.IOException | |
/** | |
* Wrapper class for [RequestBody] with a callback | |
* it overrides [writeTo] method to add an intermediate [CountingSink] | |
* and reports the progress of bytes written returned by that sink | |
* in proportion to the content length of the body through [onUploadProgress] | |
* | |
*/ | |
class ProgressFeedbackRequestBody( | |
private val requestBody: RequestBody, | |
private val onUploadProgress: (progress: Double) -> Unit | |
) : RequestBody() { | |
override fun contentType() = requestBody.contentType() | |
@Throws(IOException::class) | |
override fun contentLength() = requestBody.contentLength() | |
override fun writeTo(sink: BufferedSink) { | |
val bufferedCountingSink = | |
CountingSink( | |
sink = sink, | |
onUploadProgress = { bytesWritten -> | |
onUploadProgress(100.0 * bytesWritten / contentLength()) | |
} | |
).buffer() | |
requestBody.writeTo(bufferedCountingSink) | |
bufferedCountingSink.flush() | |
} | |
} | |
/** | |
* Wrapper class for [ResponseBody] with a callback | |
* it overrides [source] method to return a buffered [CountingSource] | |
* and reports the sum of bytes read returned by that source | |
* in proportion to the content length of the body through [onDownloadProgress] | |
* | |
*/ | |
class ProgressFeedbackResponseBody( | |
private val responseBody: ResponseBody, | |
private val onDownloadProgress: (progress: Double) -> Unit | |
) : ResponseBody() { | |
private val bufferedSource = | |
CountingSource( | |
source = responseBody.source(), | |
onDownloadProgress = { bytesReadCount -> | |
onDownloadProgress(100.0 * bytesReadCount / contentLength()) | |
} | |
).buffer() | |
override fun contentLength() = responseBody.contentLength() | |
override fun contentType() = responseBody.contentType() | |
override fun source(): BufferedSource { | |
return bufferedSource | |
} | |
} | |
/** | |
* An [Interceptor] for [okhttp3.OkHttpClient] to wrap [ResponseBody] | |
* with a [ProgressFeedbackResponseBody] | |
* | |
*/ | |
class DownloadProgressFeedbackInterceptor( | |
private val totalSize: Long, | |
private val onDownloadProgress: (progress: Double) -> Unit | |
) : Interceptor { | |
override fun intercept(chain: Interceptor.Chain): Response { | |
val originalResponse = chain.proceed(chain.request()) | |
return originalResponse.body?.let { | |
originalResponse | |
.newBuilder() | |
.body(ProgressiveResponseBody(it, onDownloadProgress, totalSize)) | |
.build() | |
} ?: run { | |
originalResponse | |
} | |
} | |
} | |
/** | |
* A [Sink] with a callback to report bytes written in a call | |
* | |
*/ | |
class CountingSink( | |
sink: Sink, | |
private val onUploadProgress: (bytesWritten: Long) -> Unit | |
) : ForwardingSink(sink) { | |
private var bytesWritten = 0L | |
override fun write(source: Buffer, byteCount: Long) { | |
super.write(source, byteCount) | |
bytesWritten += byteCount | |
onUploadProgress(bytesWritten) | |
} | |
} | |
/** | |
* A [Source] with a callback to report bytes read in a call | |
* | |
*/ | |
class CountingSource( | |
source: Source, | |
private val onDownloadProgress: (bytesReadCount: Long) -> Unit | |
) : ForwardingSource(source) { | |
private var bytesReadCount = 0L | |
override fun read(sink: Buffer, byteCount: Long): Long { | |
val bytesRead = super.read(sink, byteCount) | |
bytesReadCount = bytesReadCount.plus(if (bytesRead.toInt() != -1) bytesRead else 0) | |
onDownloadProgress(bytesReadCount) | |
return bytesRead | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment