下载进度 kotlin_在Kotlin中上传进度文件

本文介绍了如何使用Retrofit、OkHttp和Okiot在Kotlin中实现文件上传,同时实时接收上传进度并展示给用户。通过CountingRequestBody和监控Sink,我们展示了如何在文件上传过程中捕获和报告进度,适用于大型文件上传等场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

下载进度 kotlin

Do you want to upload a file using the clean Retrofit syntax, but aren’t sure how to receive the result as well as the upload progress? We will be using Retrofit to perform the file upload, building an implementation that is able to receive the completion progress at intervals and then complete with the remote API response.

您是否要使用干净的Retrofit语法上载文件,但是不确定如何接收结果以及上载进度? 我们将使用Retrofit执行文件上传,构建一个实现,该实现能够定期接收完成进度,然后完成远程API响应。

Whilst long-running operations are happening, it is nice for the user to see that activity is occurring, such as a progress view being displayed. For the case of a file upload we can show the real progress, which can be represented by the number of bytes transmitted out of the total file size.

在进行长时间运行的操作时,用户可以很好地看到正在发生的活动,例如正在显示进度视图。 对于文件上载的情况,我们可以显示实际进度,可以用文件总大小中传输的字节数表示。

We will use the APIs available to us in Retrofit, OkHttp and Okio to build a class that can be used whenever we want a request to publish its progress to whoever wishes to observe it! 🆙

我们将利用在提供给我们的API 改造OkHttp奥基奥构建可每当我们要请求发布其对凡要观察它的进步被使用的类! 🆙

Before we continue: Please check out the article on my blog, Lord Codes, you will find code snippets with themed syntax highlighting and much more, it is definitely my preferred way to read it! 👍

在继续之前:查看我博客上的文章Lord Codes ,您将找到突出显示主题语法的代码片段以及更多内容,这绝对是我首选的阅读方式! 👍

终点 (Endpoint)

We are developing a messaging application that is able to attach a file to a message thread. It is worth noting that the reactive component uses RxJava, however, it can be altered to use regular callbacks or Kotlin Coroutines and suspend functions.

我们正在开发一个消息传递应用程序,该应用程序能够将文件附加到消息线程。 值得注意的是,React式组件使用RxJava,但是可以将其更改为使用常规回调或Kotlin协程并挂起函数。

Our endpoint is a POST request that contains a multipart body, consisting of the filename, file MIME type, file size and the file itself. We can define it using Retrofit, specifying the required parts.

我们的端点是一个POST请求,其中包含一个多部分主体,由文件名,文件MIME类型,文件大小和文件本身组成。 我们可以使用翻新来定义它,指定所需的零件。

@Multipart
@POST("file")
fun attachFile(
    @Part("name") filename: RequestBody,
    @Part("type") mimeType: RequestBody,
    @Part("size") fileSize: RequestBody,
    @Part filePart: MultipartBody.Part
): Single<AttachmentUploadedRemoteDto>

计算进度 (Counting progress)

If we just wanted to upload the file without any progress, we would simply convert the file to a request body and send it in the request.

如果我们只是想上传文件而没有任何进展,我们只需将文件转换为请求正文并在请求中发送即可。

fun createUploadRequestBody(file: File, mimeType: String) = 
    file.asRequestBody(mimeType.toMediaType())

Monitoring upload progress can be achieved by using our own CountingRequestBody which wraps around the file RequestBody that would have been used before. The data that is transmitted is the same as before, allowing the raw file RequestBody to be delegated to for the content type and content length.

可以使用我们自己的CountingRequestBody来监视上载进度,该CountingRequestBody可以包装以前使用过的RequestBody文件。 传输的数据与以前相同,允许将原始文件RequestBody委托给其内容类型和内容长度。

class CountingRequestBody(
    private val requestBody: RequestBody,
    private val onProgressUpdate: CountingRequestListener
) : RequestBody() {
    override fun contentType() = requestBody.contentType()


    @Throws(IOException::class)
    override fun contentLength() = requestBody.contentLength()


    ...
}

Transmitting the request body is performed by writing it to a Sink, we will wrap the default sink with our own one that counts the bytes that are transmitted and reports them back via a progress callback.

传输请求主体是通过将其写入Sink来执行的,我们将使用我们自己的默认接收器来包装默认Sink ,该Sink器对传输的字节进行计数,并通过进度回调将其报告回去。

typealias CountingRequestListener = (bytesWritten: Long, contentLength: Long) -> Unit


class CountingSink(
    sink: Sink,
    private val requestBody: RequestBody,
    private val onProgressUpdate: CountingRequestListener
) : ForwardingSink(sink) {
    private var bytesWritten = 0L


    override fun write(source: Buffer, byteCount: Long) {
        super.write(source, byteCount)


        bytesWritten += byteCount
        onProgressUpdate(bytesWritten, requestBody.contentLength())
    }
}

Within CountingRequestBody we can wrap the default sink into our new CountingSink and write to a buffered version of that, in order to both transmit the file and observe its progress. 👀

CountingRequestBody我们可以将默认接收器包装到新的CountingSink并写入其缓冲版本,以便既传输文件又观察其进度。 👀

class CountingRequestBody(...) : RequestBody() {
    ...


    @Throws(IOException::class)
    override fun writeTo(sink: BufferedSink) {
        val countingSink = CountingSink(sink, this, onProgressUpdate)
        val bufferedSink = countingSink.buffer()


        requestBody.writeTo(bufferedSink)


        bufferedSink.flush()
    }
}

结果 (The result)

Whilst observing the upload progress, there will either be progress or a completed response, the perfect candidate for a sealed class. This will allow CountingRequestResult to be the return type and callers can handle both progress updates and the completed result.

在观察上传进度的同时,无论进度还是完整的响应,都是密封课程的理想人选。 这将使CountingRequestResult成为返回类型,并且调用者可以处理进度更新和完成的结果。

sealed class CountingRequestResult<ResultT> {
    data class Progress<ResultT>(
        val progressFraction: Double
    ) : CountingRequestResult<ResultT>()


    data class Completed<ResultT>(
        val result: ResultT
    ) : CountingRequestResult<ResultT>()
}

执行上传 (Perform the upload)

Now that we have a way of uploading a file and receiving the upload progress, we can write our FileUploader. Creating the request body for our upload request involves using a CountingRequestBody that reports progress and completion to a PublishSubject (or another reactive type).

现在,我们有了一种上传文件并接收上传进度的方法,我们可以编写FileUploader 。 为我们的上载请求创建请求主体涉及使用CountingRequestBody ,该请求将进度和完成情况报告给PublishSubject (或其他React类型)。

private fun createUploadRequestBody(
    file: File,
    mimeType: String,
    progressEmitter: PublishSubject<Double>
): RequestBody {
    val fileRequestBody = file.asRequestBody(mimeType.toMediaType())
    return CountingRequestBody(fileRequestBody) { bytesWritten, contentLength ->
        val progress = 1.0 * bytesWritten / contentLength
        progressEmitter.onNext(progress)


        if (progress >= 1.0) {
            progressEmitter.onComplete()
        }
    }
}

The upload request consists of using the Retrofit function we implemented at the beginning, providing the file details and the created request body that will count progress. The Retrofit definition and the format of the request parts will depend on how each particular API is put together. Here we are using a request that contains various plaintext parts for the file details and then one for the file to be uploaded.

上载请求包括使用我们一开始实现的Retrofit功能,提供文件详细信息和创建的请求主体(将计算进度)。 改装定义和请求部分的格式将取决于每个特定API的组合方式。 在这里,我们使用一个请求,该请求包含用于文件详细信息的各种纯文本部分,然后包含用于上载文件的一个部分。

private fun createUploadRequest(
    filename: String,
    file: File,
    mimeType: String,
    progressEmitter: PublishSubject<Double>
): Single<AttachmentUploadedRemoteDto> {
    val requestBody = createUploadRequestBody(file, mimeType, progressEmitter)
    return remoteApi.attachFile(
        filename = filename.toPlainTextBody(),
        mimeType = mimeType.toPlainTextBody(),
        fileSize = file.length().toString().toPlainTextBody(),
        filePart = MultipartBody.Part.createFormData(
            name = "files[]",
            filename = filename,
            body = requestBody
        )
    )
}


private fun String.toPlainTextBody() = toRequestBody("text/plain".toMediaType())

Our main upload function can put together all of these parts to create a single result stream. We will be able to observe this to get progress updates as well as the final result.

我们的主要上传功能可以将所有这些部分放在一起,以创建单个结果流。 我们将能够观察到这一点,以获取进度更新以及最终结果。

fun uploadAttachment(
    filename: String, file: File, mimeType: String
): Observable<AttachmentUploadRemoteResult> {
    val progressEmitter = PublishSubject.create<Double>()
    val uploadRequest = createUploadRequest(
        filename, file, mimeType, progressEmitter
    )


    val uploadResult = uploadRequest
        .map<AttachmentUploadRemoteResult> { 
            CountingRequestResult.Completed(it.result) 
        }
        .toObservable()


    val progressResult = progressEmitter
        .map<AttachmentUploadRemoteResult> { 
            CountingRequestResult.Progress(it) 
        }


    return progressResult.mergeWith(uploadResult)
}


typealias AttachmentUploadRemoteResult = 
    CountingRequestResult<AttachmentUploadedRemoteDto>

We can now upload a file to our API and update a view as the request progresses, which is nice for noticeably long operations like uploading larger files.

现在,我们可以将文件上传到我们的API并随着请求的进行更新视图,这对于明显的长操作(例如上传较大的文件)非常有用。

uploader.uploadAttachment(request.filename, request.file, request.mimeType)
    .subscribeOn(appRxSchedulers.io)
    .observeOn(appRxSchedulers.main)
    .subscribeBy(
        onError = { error ->
            // Display error alert
        },
        onComplete = {
            // Display completed Snackbar
        },
        onNext = { progress ->
            // Update progress bar
        }
    )
    .addTo(disposeBag)

结语 (Wrap up)

Monitoring the progress of a web request may not be immediately obvious when reading through the Retrofit API, however, the powerful APIs of OkHttp and Okio can get the job done nicely. The solution we have developed can be used for any web request, as the counting process can be wrapped around any RequestBody that needs to be sent in a request.

在阅读Retrofit API时,监视Web请求的进度可能不会立即显而易见,但是,强大的OkHttp和Okio API可以很好地完成工作。 我们开发的解决方案可以用于任何Web请求,因为计数过程可以包装在需要在请求中发送的任何RequestBody周围。

Do you have any requests in your apps that could benefit from observing their progress? If you already have a solution do you use something similar or do you have a different way of doing it? Please feel free to put forward any thoughts or questions you have on Twitter @lordcodes.

您的应用程序中是否有任何请求可以从观察其进度中受益? 如果您已经有了解决方案,您是否会使用类似的解决方案,或者是否有其他解决方案? 请随时在Twitter @lordcodes上提出您的任何想法或问题。

Thanks for reading and happy coding! 🙏

感谢您的阅读和愉快的编码! 🙏

翻译自: https://proandroiddev.com/uploading-a-file-with-progress-in-kotlin-6cae3aa4a2d4

下载进度 kotlin

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值