由于阿里大佬的对象存储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
也可以同时指定多个范围的内容,这种情况不是很常见。
关键代码
- 下载前校验准备
/**
* 获取下载内容的大小
* @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)
}
- 写入文件
/**
* 写入文件到本地
* @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、其他
……
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
机房租用,北京机房租用,IDC机房托管, http://www.fwqtg.net
相关推荐: .net开发者应掌握的利器CommunityToolkit.HighPerformance——MemoryOwner与SpanOwner
MemoryOwner和SpanOwner都可以理解为是对ArrayPool的一个包装,无非一个是在堆栈上,一个是在托管堆上。既然做了包装,那肯定随之而来就是改进和优化。 目录 MemoryOwnerMemoryOwner解决的问题 SpanOwner 总结 …