Last active
September 7, 2017 11:27
-
-
Save alex8224/e4204fec4ee7a3925a7385c0728a05cf to your computer and use it in GitHub Desktop.
resume download use kotlin
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.io.* | |
import java.net.URL | |
import java.net.HttpURLConnection as http | |
data class Response(val status: Int=200, var header: Map<String, List<String>>, var stream: InputStream) | |
object Request { | |
private fun method(method:String, url: String, header: Map<String, String>, followRedirect:Boolean=true): Pair<Response, http> { | |
val client = URL(url).openConnection() as http | |
client.requestMethod = method | |
client.instanceFollowRedirects = followRedirect | |
client.addHeaders(header) | |
val respHeader = client.headerFields.filter { m->m.key != null } | |
return Pair(Response(client.responseCode, respHeader, client.inputStream), client) | |
} | |
fun get(url: String, header:Map<String, String>): Pair<Response,http> { | |
return method("GET", url, header) | |
} | |
fun range(url: String, header:Map<String, String>, start:Int=0, end:Int=0): Pair<Response,http> { | |
val endIndex = if (end == 0) "" else end | |
val rangeStr = "bytes=%d-%s".format(start, endIndex) | |
val mutableMap = header as MutableMap | |
mutableMap.put("Range", rangeStr) | |
return method("GET", url, header) | |
} | |
fun head(url: String, header:Map<String, String>): Pair<Response,http> { | |
return method("HEAD", url, header) | |
} | |
} | |
val headers = mutableMapOf( | |
"User-Agent" to "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36", | |
"Accept" to "*", | |
"Range" to "bytes=0-1024", | |
"Connection" to "keep-alive", | |
"Referer" to "", | |
"Accept-Encoding" to "en;q=0.4,en-US;q=0.2", | |
"Accept-Encoding" to "gzip, deflate" | |
) | |
val name = "test" | |
fun getLength(url:String): Int? { | |
val (headResp, client) = Request.head(url, headers) | |
val status = headResp.status | |
if (status != 200) { | |
println("failed!") | |
} | |
client.close() | |
return headResp.header.get("Content-Length")?.get(0)?.toInt() | |
} | |
fun downloadFile(url:String) { | |
//获取文件信息,恢复断点 | |
val fileInfo = findFile(name) | |
if (fileInfo == null) { | |
println("info file not found") | |
return | |
} | |
val (resp, client) = Request.range(url, headers, fileInfo!!.offset!!) | |
when(resp.status) { | |
200,206 -> { | |
writeFile(client.inputStream, name, fileInfo!!.offset!!, fun (bytes: Int){ | |
println(bytes) | |
val sourceSize = fileInfo.offset!!.plus(bytes) | |
fileInfo.offset = sourceSize | |
writeFileInfo(name, fileInfo.size, sourceSize) | |
}) | |
} | |
else-> println("download failed!") | |
} | |
} | |
fun main(args:Array<String>) { | |
val url = "" | |
var fileInfo = findFile(name) | |
if(fileInfo == null) { | |
val vSize = getLength(url) | |
writeFileInfo(name, vSize, 0) | |
} | |
downloadFile(url) | |
} | |
fun http.addHeaders(headers:Map<String,String>) { | |
headers.forEach { k, v -> this.addRequestProperty(k, v) } | |
} | |
fun http.close() { | |
this.disconnect() | |
} | |
fun writeFile(stream: InputStream, name:String, offset:Int=0, callback:(bytes:Int)->Unit={}) { | |
val buff = ByteArray(65535) | |
val accessFile = RandomAccessFile(File(name), "rw") | |
val contentLength = 0 | |
accessFile.seek(offset.toLong()) | |
accessFile.use { | |
var contentLength = 0 | |
while (true) { | |
val len = stream.read(buff) | |
if (len == -1) { | |
println("exited!") | |
break | |
} | |
contentLength = contentLength + len | |
it.write(buff, 0, len) | |
callback(len) | |
} | |
} | |
println("%s length is %d".format(name, contentLength)) | |
} | |
fun findFile(name:String):FileInfo? { | |
val infoFileName = "%s.info".format(name) | |
val infoFile = File(infoFileName) | |
if (infoFile.exists()) { | |
val fileInfo = FileInfo() | |
val textInfo = infoFile.readText().split("\n") | |
textInfo.forEach { it -> | |
val pair = it.split("=") | |
when(pair[0]) { | |
"name" -> fileInfo.name = pair[1] | |
"size" -> fileInfo.size = pair[1].toInt() | |
"offset" -> fileInfo.offset = pair[1].toInt() | |
"ok" -> fileInfo.ok = pair[1].toBoolean() | |
} | |
} | |
return fileInfo | |
} | |
return null | |
} | |
fun writeFileInfo(name:String, size:Int?, offset: Int):FileInfo { | |
val infPath = "%s.info".format(name) | |
val ok = offset >0 && offset == size | |
val infContent= "name=%s\nsize=%d\noffset=%d\nok=%b".format(name, size, offset, ok) | |
File(infPath).writeText(infContent) | |
return FileInfo(name, size) | |
} | |
data class FileInfo(var name:String?=null, var size:Int?=0, var offset: Int?=0, var ok:Boolean?=false) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment