Skip to content
This repository has been archived by the owner on Jun 6, 2023. It is now read-only.

Commit

Permalink
支持提供crc32哈希算法的结构文件
Browse files Browse the repository at this point in the history
  • Loading branch information
asforest committed Sep 27, 2022
1 parent 872dd0f commit c2d75df
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 45 deletions.
66 changes: 43 additions & 23 deletions src/main/kotlin/File2.kt
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
//@file:JvmName("FileObj")
@file:JvmName("File2")

import java.io.File
import java.io.FileInputStream
import java.io.FileNotFoundException
import java.io.FileOutputStream
import java.nio.file.Paths
import java.security.MessageDigest
import java.util.zip.CRC32
import kotlin.io.path.pathString

class File2 : Iterable<File2>
{
private var file: File
var file: File

constructor(file: String): this(File(file))

Expand Down Expand Up @@ -178,38 +179,57 @@ class File2 : Iterable<File2>

val md5: String get() = hash("MD5")

private fun hash(method: String): String
{
val bufferLen = { filelen: Long ->
val kb = 1024
val mb = 1024 * 1024
val gb = 1024 * 1024 * 1024
when {
filelen < 1 * mb -> 8 * kb
filelen < 2 * mb -> 16 * kb
filelen < 4 * mb -> 32 * kb
filelen < 8 * mb -> 64 * kb
filelen < 16 * mb -> 256 * kb
filelen < 32 * mb -> 512 * kb
filelen < 64 * mb -> 1 * mb
filelen < 128 * mb -> 2 * mb
filelen < 256 * mb -> 4 * mb
filelen < 512 * mb -> 8 * mb
filelen < 1 * gb -> 16 * mb
else -> 32 * mb
}
val crc32: String get() {
val crc32calculator = CRC32()
FileInputStream(file).use {
var len = 0
val buf = ByteArray(chooseBufferLength(length))
while (it.read(buf).also { len = it } != -1)
crc32calculator.update(buf, 0, len)
}

val value = crc32calculator.value
val array = ByteArray(4)
array[3] = (value shr (8 * 0) and 0xFF).toByte()
array[2] = (value shr (8 * 1) and 0xFF).toByte()
array[1] = (value shr (8 * 2) and 0xFF).toByte()
array[0] = (value shr (8 * 3) and 0xFF).toByte()
return bin2str(array)
}

private fun hash(method: String): String
{
val md = MessageDigest.getInstance(method)
FileInputStream(file).use {
var len = 0
val buf = ByteArray(bufferLen(length))
val buf = ByteArray(chooseBufferLength(length))
while (it.read(buf).also { len = it } != -1)
md.update(buf, 0, len)
}
return bin2str(md.digest())
}

private fun chooseBufferLength(length: Long): Int
{
val kb = 1024
val mb = 1024 * 1024
val gb = 1024 * 1024 * 1024
return when {
length < 1 * mb -> 8 * kb
length < 2 * mb -> 16 * kb
length < 4 * mb -> 32 * kb
length < 8 * mb -> 64 * kb
length < 16 * mb -> 256 * kb
length < 32 * mb -> 512 * kb
length < 64 * mb -> 1 * mb
length < 128 * mb -> 2 * mb
length < 256 * mb -> 4 * mb
length < 512 * mb -> 8 * mb
length < 1 * gb -> 16 * mb
else -> 32 * mb
}
}

private fun bin2str(binary: ByteArray): String
{
fun cvt (num: Byte): String
Expand Down
6 changes: 3 additions & 3 deletions src/main/kotlin/FileDiff.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* @param base 基准目录,用来计算相对路径
* @param hashCacher hash缓存对象
*/
class FileDiff(val current: VirtualFile, val contrast: File2, val base: File2, val hashCacher: HashCacher)
class FileDiff(val current: VirtualFile, val contrast: File2, val base: File2, val hashCacher: HashCacher, val useSha1: Boolean)
{
private val differences: Difference = Difference()

Expand All @@ -22,7 +22,7 @@ class FileDiff(val current: VirtualFile, val contrast: File2, val base: File2, v

if(corresponding == null) // 如果文件不存在的话,就不用校验了,可以直接进行下载
{
markAsNew(VirtualFile.fromRealFile(c), c)
markAsNew(VirtualFile.fromRealFile(c, useSha1), c)
continue
}

Expand Down Expand Up @@ -81,7 +81,7 @@ class FileDiff(val current: VirtualFile, val contrast: File2, val base: File2, v
if(a.modified == b.modified)
return true

return if(hashCacher.getHash(b.relativizedBy(base)) != a.hash) {
return if(hashCacher.getHash(b.relativizedBy(base), useSha1) != a.hash) {
false
} else {
b._file.setLastModified(a.modified)
Expand Down
11 changes: 7 additions & 4 deletions src/main/kotlin/HashCacher.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,17 @@ class HashCacher(val base: File2)
{
private val cache = mutableMapOf<String, String>()

fun getHash(relativePath: String): String
fun getHash(relativePath: String, useSha1: Boolean): String
{
if (relativePath !in cache)
val key = (if (useSha1) "sha1|" else "crc32|") + relativePath

if (key !in cache)
{
val file = base + relativePath
cache[relativePath] = file.sha1
cache[key] = if (useSha1) file.sha1 else file.crc32
println(cache[key])
}

return cache[relativePath]!!
return cache[key]!!
}
}
38 changes: 26 additions & 12 deletions src/main/kotlin/Server.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import kotlin.collections.LinkedHashMap
class Server(val config: AppConfig) : NanoHTTPD(config.host, config.port)
{
private val fmt = SimpleDateFormat("YYYY-MM-dd HH:mm:ss")
private var structureInfoCache: String? = null
private var structureCacheInSha1: String? = null
private var structureCacheInCrc32: String? = null

/**
* 服务主函数
Expand Down Expand Up @@ -51,11 +52,16 @@ class Server(val config: AppConfig) : NanoHTTPD(config.host, config.port)
{
val ne = LinkedHashMap<String, Any>()
ne["update"] = "res"
ne.putAll(config.configYaml.filter { it.key == "common_mode" || it.key == "once_mode" })
ne["common_mode"] = config.configYaml["common_mode"] as List<*>
ne["once_mode"] = config.configYaml["once_mode"] as List<*>
ne["hash_algorithm"] = "crc32"
return ResponseHelper.buildJsonTextResponse(JSONObject(ne).toString(4))
} else if (path == "/res.json") {
regenResCache()
return ResponseHelper.buildJsonTextResponse(structureInfoCache!!)
regenResSha1Cache()
return ResponseHelper.buildJsonTextResponse(structureCacheInSha1!!)
} else if (path == "/res_crc32.json") {
regenResCrc32Cache()
return ResponseHelper.buildJsonTextResponse(structureCacheInCrc32!!)
} else if (dir != null) { // 禁止访问任何目录
return ResponseHelper.buildForbiddenResponse("Directory is unable to show")
} else if (!path.startsWith("/res/")) { // 不能访问res目录以外的文件
Expand All @@ -80,26 +86,34 @@ class Server(val config: AppConfig) : NanoHTTPD(config.host, config.port)
}

/**
* 生成res目录缓存
* 生成res目录缓存(sha1算法)
*/
private fun regenResCache()
private fun regenResSha1Cache()
{
structureInfoCache = JSONArray().also { j -> genCache(config.baseDir + "res").forEach { j.put(it.toJsonObject()) } }.toString()
structureCacheInSha1 = JSONArray().also { j -> genCache(config.baseDir + "res", true).forEach { j.put(it.toJsonObject()) } }.toString()
}

/**
* 生成res目录缓存(crc32算法)
*/
private fun regenResCrc32Cache()
{
structureCacheInCrc32 = JSONArray().also { j -> genCache(config.baseDir + "res", false).forEach { j.put(it.toJsonObject()) } }.toString()
}

/**
* 生成文件结构信息
*/
private fun genCache(directory: File2): List<VirtualFile>
private fun genCache(directory: File2, useSha1: Boolean): List<VirtualFile>
{
fun getDirname(path: String): String? = path.lastIndexOf("/").run { if (this == -1) null else path.substring(0, this) }
fun getBasename(path: String): String = path.lastIndexOf("/").run { if (this == -1) path else path.substring(this + 1) }

val baseDir = config.baseDir + "res"
val hashCacher = HashCacher(baseDir)
val cacheFile = File2("cache.json")
val cacheFile = File2(if (useSha1) "cache.json" else "cache_crc32.json")
val cache = cacheFile.run { if (exists) VirtualFile.fromJsonArray(JSONArray(content), "no_name") else null }
val diff = cache?.run { FileDiff(this, directory, baseDir, hashCacher).compare() }
val diff = cache?.run { FileDiff(this, directory, baseDir, hashCacher, useSha1).compare() }

// 更新res.json缓存
if (diff?.hasDifferences() == true)
Expand Down Expand Up @@ -133,13 +147,13 @@ class Server(val config: AppConfig) : NanoHTTPD(config.host, config.port)
val file = baseDir + f
val length = file.length
val modified = file.modified
val hash = hashCacher.getHash(f)
val hash = hashCacher.getHash(f, useSha1)

dir.files += VirtualFile(filename, length, hash, modified)
}
}

val result = cache?.files ?: VirtualFile.fromRealFile(directory).files
val result = cache?.files ?: VirtualFile.fromRealFile(directory, useSha1).files

// 落盘
if (diff == null || diff.hasDifferences())
Expand Down
6 changes: 3 additions & 3 deletions src/main/kotlin/VirtualFile.kt
Original file line number Diff line number Diff line change
Expand Up @@ -129,12 +129,12 @@ class VirtualFile
}

@JvmStatic
fun fromRealFile(file: File2): VirtualFile
fun fromRealFile(file: File2, useSha1: Boolean): VirtualFile
{
return if (file.isDirectory) {
VirtualFile(file.name, files = file.files.map { fromRealFile(it) })
VirtualFile(file.name, files = file.files.map { fromRealFile(it, useSha1) })
} else {
VirtualFile(file.name, length = file.length, hash = file.sha1, modified = file.modified)
VirtualFile(file.name, length = file.length, hash = if (useSha1) file.sha1 else file.crc32, modified = file.modified)
}
}
}
Expand Down

0 comments on commit c2d75df

Please sign in to comment.