实现目标
- 实现对大文件进行分片下载,分片下载完成后进行数据合并,成为一个完整的文件;
- 后续目标“协程”
成果展示
maven配置文件
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test-junit5</artifactId>
<version>1.7.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
<version>1.7.10</version>
</dependency>
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>retrofit</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>converter-gson</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>logging-interceptor</artifactId>
<version>4.10.0</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-core</artifactId>
<version>1.6.4</version>
</dependency>
</dependencies>
主程序
package com.jack.demo
import com.google.gson.Gson
import com.jack.demo.thread.DownLoadTask
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import java.io.File
import java.util.concurrent.Future
import java.util.concurrent.LinkedBlockingDeque
import java.util.concurrent.ThreadPoolExecutor
import java.util.concurrent.TimeUnit
var gson: Gson = Gson()
//切片大小
var slicesSize = 10 * 1024 * 1024
var basePath = "D:\www\test\"
//线程池
val pool = ThreadPoolExecutor(
2, 200,
120L, TimeUnit.SECONDS,
LinkedBlockingDeque(),//无界,FIFO队列
ThreadPoolExecutor.AbortPolicy()
);
fun main(args: Array<String>) {
//资源地址
var url = "http://127.0.0.1:8099/file/v.mp4"
//构造请求
var request: Request = Request.Builder().url(url).get().build()
//获取资源长度
val client = OkHttpClient()
var response: Response = client.newCall(request).execute()
var dataLength: Int? = response.body()?.contentLength()?.toInt()
println("要下载的文件长度:$dataLength ${response.headers().get("Content-Type")}")
//进行分片
println("切片阈值:$slicesSize")
var list = calculationSlices(dataLength!!)
println("切片数量:${list?.size} 切片详情:" + gson.toJson(list))
//提交分片下载任务
var futures = ArrayList<Future<String>>()
for ((index, value) in list!!.withIndex()) {
futures.add(pool.submit(DownLoadTask(value.startIndex, value.endIndex, basePath + index)))
}
//获取分片下载结果
for (future in futures){
println("子任务下载结果:"+future.get(10,TimeUnit.SECONDS))
}
//合并成完整视频
var filePath = dataLength?.let { combineVideo(list.size, it) }
println("完整视频路径 $filePath")
System.exit(0)
}
/**
* 合并为完整视频并返回视频地址
*/
fun combineVideo(size: Int, dataLength: Int): String {
var bts = ByteArray(0)
for (index in 0 until size) {
val myFile = File(basePath + index)
var bytes = myFile.inputStream().readBytes()
bts = bts.plus(bytes)
}
var filePath = basePath + "aa.mp4";
var file = File(filePath)
//保存至文件中
bts?.let { file.writeBytes(it) }
return filePath
}
/**
* 分片对象
*/
class Slices(var startIndex: Int, var endIndex: Int)
/**
* 计算分片信息
*/
fun calculationSlices(dataLength: Int): ArrayList<Slices> {
var result = ArrayList<Slices>()
var parks: Int = dataLength / slicesSize
var over: Int = dataLength % slicesSize
if (over >= 0) parks += 1
var startIndex = 0
for (index in 0 until parks) {
var endIndex = startIndex + slicesSize
endIndex = if (endIndex > dataLength) dataLength else endIndex
var slices = Slices(startIndex, endIndex)
result.add(slices)
startIndex = endIndex + 1
}
return result
}
分片下载任务
package com.jack.demo.thread
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import java.io.File
import java.util.concurrent.Callable
class DownLoadTask(var startIndex: Int, var endIndex: Int, var filePath: String) : Callable<String> {
override fun call(): String {
var url = "http://127.0.0.1:8099/file/v.mp4"
//构造请求
var request: Request = Request.Builder().url(url).header("Range", "bytes=$startIndex-$endIndex").get().build()
//获取资源长度
val client = OkHttpClient()
var response: Response = client.newCall(request).execute()
var dataBytes = response.body()?.bytes()
var file = File(filePath)
//保存至文件中
dataBytes?.let { file.writeBytes(it) }
println("线程ID:${Thread.currentThread().id} 下载完成 切片[$startIndex,$endIndex] 文件保存成功:$filePath")
return "success"
}
}