Download File in Android with Kotlin
目标: 需要返回 下载结果 ,下载进度, 任务可取消:
之前的写法:
/**
* 下载
* @param url 图片地址
* @param savePath 保存文件路径
* @return true 下载成功
*/
suspend fun download(url: String, savePath: String, progressCallBack: ((Double) -> Unit)? = null) = withContext(Dispatchers.IO) {
suspendCancellableCoroutine<Boolean> {//开启可取消的任务
val file = File(savePath)
var fos: FileOutputStream? = null
var bis: BufferedInputStream? = null
var stream: InputStream? = null
it.invokeOnCancellation { throwable ->
if (throwable is CancellationException) {
Pug.d(TAG, "Cancel downLoad")
try {
file.delete()
fos?.close()
bis?.close()
stream?.close()
} catch (e: Exception) {
}
}
}
try {
Pug.d(TAG, "request start")
val body = ToolRetrofit.toolApiTimeOutWith3Minute.downloadFile(url)
.execute().body()
Pug.d(TAG, "request end")
Pug.d(TAG, "save start")
stream = body?.byteStream()
val totalSize = body?.contentLength()?.toDouble() ?: 0.0
fos = FileOutputStream(file)
bis = BufferedInputStream(stream)
val buffer = ByteArray(1024)
var len: Int
var currentSize = 0.0
while (bis.read(buffer).also { len = it } != -1) {
fos.write(buffer, 0, len)
currentSize += len
progressCallBack?.invoke(currentSize / totalSize)
}
fos.flush()
fos.close()
bis.close()
stream?.close()
Pug.d(TAG, "save end")
it.resume(true)
} catch (e: Exception) {
e.printStackTrace()
it.resume(false)
} finally {
try {
fos?.close()
bis?.close()
stream?.close()
} catch (e: Exception) {
}
}
}
}
提示: Retrofit 需要用 @Streaming 来支持流下载
外面可以用 job.cancel 来取消文件下载
使用:
val job = launch{
val result = ImageDownloader.download(imgUrl, beautyTempPath) {it ->
dialog?.updateProgress(it)
}
}
//取消任务:
job.cancel()
使用Use可以精简写法:
/**
* 下载比较简单的写法:可取消
* @param onProgressChange 返回简单 [0.0-1.0】
* @return true 下载成功
*/
suspend fun download3(url: String, savePath: String,onProgressChange: ((Double) -> Unit)? = null): Boolean = withContext(Dispatchers.IO) {
val file = File(savePath)
try {
val body = ToolRetrofit.toolApi.downloadFile(url).execute().body()?:return@withContext false
val fileSize = body.contentLength().toDouble()
body.byteStream().use {
FileOutputStream(file).use { out ->
//it.copyTo(targetOutputStream) 如果不需要任务取消可以用 copyTo
var bytesCopied: Long = 0
val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
var bytes = it.read(buffer)
while (bytes >= 0 && isActive) {
out.write(buffer, 0, bytes)
bytesCopied += bytes
bytes = it.read(buffer)
val percent = bytesCopied / fileSize
onProgressChange?.invoke(percent)
}
}
}
return@withContext true
} catch (e: Exception) {
e.printStackTrace()
file.delete()//删除失败的文件
return@withContext false
}
}
fun download(url: String, file: File): Observable<Int> {
return Observable.create<Int> { emitter ->
val request = Request.Builder().url(url).build()
val response = okHttpClient.newCall(request).execute()
val body = response.body
val responseCode = response.code
if (responseCode >= HttpURLConnection.HTTP_OK &&
responseCode < HttpURLConnection.HTTP_MULT_CHOICE &&
body != null) {
val length = body.contentLength()
body.byteStream().apply {
file.outputStream().use { fileOut ->
var bytesCopied = 0
val buffer = ByteArray(BUFFER_LENGTH_BYTES)
var bytes = read(buffer)
while (bytes >= 0) {
fileOut.write(buffer, 0, bytes)
bytesCopied += bytes
bytes = read(buffer)
emitter.onNext(
((bytesCopied * 100)/length).toInt())
}
}
emitter.onComplete()
}
} else {
// Report the error
}
}
}
使用的地方:
fileDownloader.download(URL, targetFile)
.throttleFirst(2, TimeUnit.SECONDS)
.toFlowable(BackpressureStrategy.LATEST)
.subscribeOn(Schedulers.io())
.observeOn(mainThread())
.subscribe({
// onNext: Downloading in progress
reportStatus(it)
}, {
// onError: Download Error
reportError(it)
}, {
// onComplete: Download Complete
showFile(targetFile)
})
如果需要取消:
disposable = fileDownloader.download(URL, targetFile)
override fun onDestroy() {
super.onDestroy()
disposable.dispose()
}
参考: Download File in Android with Kotlin