OkHttp下载和上传文件进度监听

3,684 阅读1分钟

OkHttp的用法在此不讲

一些提前准备的代码

typealias ProgressListener = (progress: Int)->Unit

下载进度监听:对ResponseBody的包装,本质是对Source包装,在其read方法中实时获取已读取字节大小

/**
 * 在Source.read()中监听下载进度
 */
class ProgressResponseBody(
    private val responseBody: ResponseBody,
    private val l: ProgressListener
): ResponseBody() {

    private val bufferedSource: BufferedSource by lazy {
        getWrappedSource(responseBody.source()).buffer()
    }
    private var progressListener: ProgressListener? = null

    override fun contentLength(): Long = responseBody.contentLength()

    override fun contentType(): MediaType? = responseBody.contentType()

    override fun source(): BufferedSource = bufferedSource

    private fun getWrappedSource(source: Source): Source = object : ForwardingSource(source) {

        private var bytesRead = 0L
        private val contentLength by lazy {
            contentLength() //避免重复调用
        }

        override fun read(sink: Buffer, byteCount: Long): Long {
            val bytes = super.read(sink, byteCount)
            if (bytes != -1L)
                bytesRead += bytes
            //进度监听回调
            val progress = bytesRead * 100 / contentLength
            progressListener?.invoke(progress.toInt())
            return bytes
        }
    }
}

使用

OkHttpClient.Builder()
    .addNetworkInterceptor {
        val response = it.proceed(it.request())
        val body = response.body() ?: return@addNetworkInterceptor response
        val pBody = ProgressResponseBody(body) { progress ->
            //利用progress值更新UI或处理其它业务逻辑
        }
        response.newBuilder()
            .body(pBody)
            .build()
    }

上传进度监听:对RequestBody的包装,本质是对Sink包装,在其writeTo方法中实时获取已写入字节大小


/**
 * 在Sink.write()中监听上传进度
 */
class ProgressRequestBody(
    private val requestBody: RequestBody,
    private val l: ProgressListener
): RequestBody() {

    private var progressListener: ProgressListener? = null
    private lateinit var bufferedSink: BufferedSink

    override fun contentType(): MediaType? = requestBody.contentType()

    override fun contentLength(): Long = requestBody.contentLength()

    override fun writeTo(sink: BufferedSink) {
        if (!this::bufferedSink.isInitialized)
            bufferedSink = getWrappedSink(sink).buffer()
        requestBody.writeTo(bufferedSink)
        bufferedSink.flush()
    }

    private fun getWrappedSink(sink: Sink): Sink = object : ForwardingSink(sink) {

        private var bytesWritten = 0L
        private val contentLength by lazy {
            contentLength()
        }

        override fun write(source: Buffer, byteCount: Long) {
            super.write(source, byteCount)
            bytesWritten += byteCount
            val progress = bytesWritten * 100 / contentLength
            progressListener?.invoke(progress.toInt())
        }
    }
}

使用

val file = File(path)
val fileBody = RequestBody.create(MediaType.parse("multipart/form-data"), file)
//最终要post的requestBody
val requestBody = ProgressRequestBody(fileBody) { progress ->
    //利用progress值更新UI或处理其它业务逻辑
}

参考链接:blog.csdn.net/sbsujjbcy/a…