Android - OkHttp实现的断点续传

Posted 沈页

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android - OkHttp实现的断点续传相关的知识,希望对你有一定的参考价值。

由于阿里大佬的对象存储OSS服务安卓端没有断点续传的功能,看了一下ios端的实现方案,结合网上的资料整理和实现了安卓端的断点续传。

参考 developer.aliyun.com/article/630…

关键要点

在发起HTTP请求中加入Range请求头

  • Range: bytes=100- 从 101 bytes 之后开始传,一直传到最后。
  • Range: bytes=100-200 指定开始到结束这一段的长度,记住 Range 是从 0 计数 的,所以这个是要求服务器从 101 字节开始传,一直到 201 字节结束。这个一般用来特别大的文件分片传输,比如视频。
  • Range: bytes=-100 如果范围没有指定开始位置,就是要服务器端传递倒数 100 字节的内容。而不是从 0 开始的 100 字节。
  • Range: bytes=0-100, 200-300 也可以同时指定多个范围的内容,这种情况不是很常见。

关键代码

  1. 下载前校验准备
/**
 * 获取下载内容的大小
 * @param downloadUrl 文件地址
 * @return 文件字节长度
 */
private fun getContentLength(downloadUrl: String): Long 
    val request = Request.Builder().url(downloadUrl).build()
    try 
        val response = client.newCall(request).execute()
        if (response != null && response.isSuccessful) 
            val contentLength = response.body()?.contentLength() ?: 0L
            response.body()?.close()
            return contentLength
        
     catch (e: IOException) 
        e.printStackTrace()
    
    return 0L


/**
 * 下载请求
 * @param fileName 文件名称
 * @param sourceFileName 文件名称
 * @param fileUrl 文件完整地址
 */
private fun downLoadFile(sourceFileName: String,fileName:String, fileUrl: String) 
    //已经下载的文件长度,初始化为0
    //(续载的关键点 = 每次下载的起始位置)
    var downloadLength = 0L
    //下载路径 + 文件名 在本地创建一个文件
    val file = File(downloadFileCacheDir + sourceFileName)
    if (file.exists()) 
        //如果文件存在,得到文件大小
        downloadLength = file.length()
    
    //得到下载内容的大小
    val contentLength = getContentLength(fileUrl)
    if (contentLength == 0L) 
        //从内存中移除下载任务
        ossFileMap.remove(fileName)
        downLoadTaskMap.remove(fileName)
        onListenerCallBackError(ErrorCode.ERROR_DATA_NULL, "文件错误,长度为0")
        return
     else if (contentLength == downloadLength) 
        //已下载字节和文件总字节相等,说明已经下载完成了
        ossFileMap.remove(fileName)
        downLoadTaskMap.remove(fileName)
        onListenerCallBackProcess(fileName, contentLength, contentLength)
        onListenerCallBackSuccess(fileName)
        return
    

    val request = Request.Builder()
            .url(fileUrl)
            .addHeader("RANGE", "bytes=$downloadLength-")//续传关键(请求头增加)
            .build()
    Logger.d("Down下载:Range = $downloadLength")
    val call = client.newCall(request)
    //加入传载队列
    downLoadTaskMap[fileName] = call
    saveFile(call,file, downloadLength, fileName, contentLength)
 
  1. 写入文件
/**
 * 写入文件到本地
 * @param call 请求回调
 * @param file 需要写入的本地文件
 * @param downloadLength 已下载文件长度
 * @param contentLength 文件总长度
 */
private fun saveFile(call: Call, file: File?, downloadLength: Long, fileName: String, contentLength: Long) 
    var inputStream: InputStream? = null
    var savedFile: RandomAccessFile? = null
    val response = call.execute()
    try 
        if (response != null) 
            inputStream = response.body()?.byteStream()
            savedFile = RandomAccessFile(file, "rw")
            //跳过已经下载的字节
            savedFile.seek(downloadLength)
            //每次缓存字节数
            val byteArray = ByteArray(1024)
            var total = 0
            var len: Int = -1
            while (inputStream?.read(byteArray)?.also  len = it  != -1) 
                //执行缓存前都需判断该任务是否被暂停
                if (call.isCanceled) 
                    Logger.d("Down:$fileName下载任务已取消")
                    return
                 else 
                    total += len
                    savedFile.write(byteArray, 0, len)
                    //本次下载总长
                    val currentSize = total + downloadLength
                    //回调进度
                    onListenerCallBackProcess(fileName, currentSize, contentLength)
                    //长度相等则返回成功
                    if (currentSize == contentLength) 
                        response.body()?.close()
                        Logger.d("Down:success")
                        //文件与本地路径字典记录到数据库
                        filesLocalService.saveDownPathData(DownLoadFilesValue().apply 
                            this.fileName = fileName
                            this.filePath = file?.path
                        )
                        ossFileMap.remove(fileName)
                        downLoadTaskMap.remove(fileName)
                        //删除本地传载列表
                        deleteFormLocal(fileName)
                        //成功回调
                        onListenerCallBackSuccess(fileName)
                    
                
            
            response.body()?.close()
        
     catch (e: Exception) 
        //发生异常时关闭该下载任务
        ossFileMap.remove(fileName)
        downLoadTaskMap.remove(fileName)
        //失败回调
        onListenerCallBackError(ErrorCode.ERROR_EXCEPTION, e.message)
     finally 
        try 
            inputStream?.close()
            savedFile?.close()
         catch (e: Exception) 
            e.printStackTrace();
        
    
 

最后

按照国际惯例,给大家分享一套十分好用的android进阶资料:《全网最全Android开发笔记》。

整个笔记一共8大模块、729个知识点,3382页,66万字,可以说覆盖了当下Android开发最前沿的技术点,和阿里、腾讯、字节等等大厂面试看重的技术。

因为所包含的内容足够多,所以,这份笔记不仅仅可以用来当学习资料,还可以当工具书用。

如果你需要了解某个知识点,不管是Shift+F 搜索,还是按目录进行检索,都能用最快的速度找到你要的内容。

相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照整个知识体系编排的。

(一)架构师必备Java基础

1、深入理解Java泛型

2、注解深入浅出

3、并发编程

4、数据传输与序列化

5、Java虚拟机原理

6、高效IO

……

(二)设计思想解读开源框架

1、热修复设计

2、插件化框架设计

3、组件化框架设计

4、图片加载框架

5、网络访问框架设计

6、RXJava响应式编程框架设计

……

(三)360°全方位性能优化

1、设计思想与代码质量优化

2、程序性能优化

  • 启动速度与执行效率优化
  • 布局检测与优化
  • 内存优化
  • 耗电优化
  • 网络传输与数据储存优化
  • APK大小优化

3、开发效率优化

  • 分布式版本控制系统Git
  • 自动化构建系统Gradle

……

(四)Android框架体系架构

1、高级UI晋升

2、Android内核组件

3、大型项目必备IPC

4、数据持久与序列化

5、Framework内核解析

……

(五)NDK模块开发

1、NDK开发之C/C++入门

2、JNI模块开发

3、Linux编程

4、底层图片处理

5、音视频开发

6、机器学习

……

(六)Flutter学习进阶

1、Flutter跨平台开发概述

2、Windows中Flutter开发环境搭建

3、编写你的第一个Flutter APP

4、Flutter Dart语言系统入门

……

(七)微信小程序开发

1、小程序概述及入门

2、小程序UI开发

3、API操作

4、购物商场项目实战

……

(八)kotlin从入门到精通

1、准备开始

2、基础

3、类和对象

4、函数和lambda表达式

5、其他

……

好啦,这份资料就给大家介绍到这了,有需要详细文档的小伙伴,可以微信扫下方二维码免费领取哈~

以上是关于Android - OkHttp实现的断点续传的主要内容,如果未能解决你的问题,请参考以下文章

使用OkHttp实现下载的进度监听和断点续传

用AsyncTask实现断点续传

OSS android 断点续传,不知道怎么上传文件写入内存,没有被回收掉

Android开发——断点续传原理以及实现

Linux下实现断点续传的原理介绍

Android入门第56天-在Android里使用OKHttp多线程下载文件并展示其进度