|
@@ -59,15 +59,16 @@ object UploadUtil {
|
|
|
// 文件分片 如果分片小于chunkSize && totalChunk = 1,直接转存
|
|
|
if (fileMetadata.totalChunks == 1 && fileMetadata.chunkSize!! <= chunkSize) {
|
|
|
val filename = fileMetadata.filename!!
|
|
|
- val relativePath = fileMetadata.relativePath!!
|
|
|
-
|
|
|
- val destPath = FilenameUtils.normalize("$dataPath$FILE_SEPARATOR${getDestFilePath(filename, relativePath, fileMetadata.repath)}")
|
|
|
- val destDir = Paths.get(destPath)
|
|
|
- if (Files.notExists(destDir)) {
|
|
|
- Files.createDirectories(destDir)
|
|
|
+ val md5 = fileMetadata.md5!!
|
|
|
+
|
|
|
+ // 目标文件流 通过文件名来拼接
|
|
|
+ val outputFullFilename = getFullDestFilename(dataPath, filename, md5)
|
|
|
+ val outputPath = Paths.get(FilenameUtils.getFullPath(outputFullFilename))
|
|
|
+ // 不存在则创建文件夹
|
|
|
+ if (Files.notExists(outputPath)) {
|
|
|
+ Files.createDirectories(outputPath)
|
|
|
}
|
|
|
- val outputPath = FilenameUtils.normalize("$destPath$FILE_SEPARATOR${getDestFileName(filename, fileMetadata.rename)}")
|
|
|
- retFile = File(outputPath)
|
|
|
+ retFile = File(outputFullFilename)
|
|
|
file.transferTo(retFile)
|
|
|
retFile.setLastModified(fileMetadata.lastModified!!)
|
|
|
} else {
|
|
@@ -102,11 +103,15 @@ object UploadUtil {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 设置批次文件数量信息 ++
|
|
|
+ */
|
|
|
private fun setBatch(fileMetadata: ZenFileMetadata): String {
|
|
|
val batchId = fileMetadata.batchId!!
|
|
|
val totalNumber = fileMetadata.totalNumber!!
|
|
|
synchronized(batchCountMap) {
|
|
|
var count = batchCountMap[batchId]?.get() as? Int ?: 0
|
|
|
+ logger.debug("batchId: $batchId, count: ${count + 1}, totalNumber: $totalNumber")
|
|
|
return if (++count >= totalNumber) {
|
|
|
batchCountMap.evict(batchId)
|
|
|
STATUS.BATCH_UPLOADED
|
|
@@ -119,18 +124,20 @@ object UploadUtil {
|
|
|
|
|
|
/**
|
|
|
* 检测文件是否存在 实现文件秒传
|
|
|
+ * 真实文件已存在,秒传
|
|
|
+ * 真实文件不存在,检查分片上传情况
|
|
|
*/
|
|
|
fun checkUpload(fileMetadata: ZenFileMetadata): ZenFileResponse {
|
|
|
+ val tmpPath = uploadProperties!!.tmpPath!!
|
|
|
val dataPath = uploadProperties!!.dataPath!!
|
|
|
- val ret = ZenFileResponse()
|
|
|
+ val ret = ZenFileResponse().apply { status = STATUS.CHECKING }
|
|
|
if (validateRequest(fileMetadata, null)) {
|
|
|
if (fileExists(fileMetadata)) {
|
|
|
ret.uploadedChunks = mutableListOf()
|
|
|
for (i in 1..fileMetadata.totalChunks!!) {
|
|
|
- ret.uploadedChunks?.add(i)
|
|
|
+ ret.uploadedChunks!!.add(i)
|
|
|
}
|
|
|
- ret.status = setBatch(fileMetadata)
|
|
|
- ret.file = File(getFullDestFilename(dataPath, fileMetadata.filename!!, fileMetadata.relativePath!!, fileMetadata.repath, fileMetadata.rename))
|
|
|
+ ret.file = File(getFullDestFilename(dataPath, fileMetadata.filename!!, fileMetadata.md5!!))
|
|
|
} else {
|
|
|
// 检查分片存在情况
|
|
|
for (i in 1..fileMetadata.totalChunks!!) {
|
|
@@ -138,15 +145,28 @@ object UploadUtil {
|
|
|
if (ret.uploadedChunks == null) {
|
|
|
ret.uploadedChunks = mutableListOf()
|
|
|
}
|
|
|
- ret.uploadedChunks?.add(i)
|
|
|
+ ret.uploadedChunks!!.add(i)
|
|
|
}
|
|
|
}
|
|
|
if (ret.uploadedChunks != null && ret.uploadedChunks!!.size == fileMetadata.totalChunks) {
|
|
|
- ret.status = STATUS.CHECKING // 秒传
|
|
|
- // 所有分块传完 移除其中一个分块,再传一次而后 merge
|
|
|
- ret.uploadedChunks!!.removeAt(0)
|
|
|
+ // 合并
|
|
|
+ ret.file = try {
|
|
|
+ mergeChunks(tmpPath, dataPath, fileMetadata)
|
|
|
+ } catch (e: Throwable) {
|
|
|
+ logger.error("merge file chunks exception, cause ", e)
|
|
|
+ uploadedStatusMap.remove(fileMetadata.md5!!)
|
|
|
+ null
|
|
|
+ }
|
|
|
+
|
|
|
+// // 所有分块传完 移除其中一个分块,再传一次而后 merge
|
|
|
+// ret.uploadedChunks!!.removeAt(0)
|
|
|
}
|
|
|
}
|
|
|
+ } else {
|
|
|
+ throw ZenException(ZenExceptionEnum.FILE_METADATA_VALIDATE_ERROR)
|
|
|
+ }
|
|
|
+ if (ret.file != null) {
|
|
|
+ ret.status = setBatch(fileMetadata)
|
|
|
}
|
|
|
return ret
|
|
|
}
|
|
@@ -158,10 +178,9 @@ object UploadUtil {
|
|
|
val dataPath = uploadProperties!!.dataPath!!
|
|
|
// 获取filename 真实文件
|
|
|
val filename = fileMetadata.filename!!
|
|
|
- val relativePath = fileMetadata.relativePath!!
|
|
|
val md5 = fileMetadata.md5!!
|
|
|
- val path = getFullDestFilename(dataPath, filename, relativePath, fileMetadata.repath, fileMetadata.rename)
|
|
|
- return Files.exists(Paths.get(path)) && FileUtil.md5HeadTail(path, uploadProperties!!.chunkSize!!.toInt()) == md5 // # 防篡改
|
|
|
+ val realFilename = getFullDestFilename(dataPath, filename, md5)
|
|
|
+ return Files.exists(Paths.get(realFilename)) && FileUtil.md5HeadTail(realFilename, uploadProperties!!.chunkSize!!.toInt()) == md5 // # 防篡改
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -193,7 +212,6 @@ object UploadUtil {
|
|
|
private fun mergeChunks(sourceRootPath: String, destRootPath: String, fileMetadata: ZenFileMetadata): File? {
|
|
|
// 源文件的文件夹信息 然后组合拼接
|
|
|
val filename = fileMetadata.filename!!
|
|
|
- val relativePath = fileMetadata.relativePath!!
|
|
|
val totalChunks = fileMetadata.totalChunks!!
|
|
|
val md5 = fileMetadata.md5!!
|
|
|
|
|
@@ -206,14 +224,14 @@ object UploadUtil {
|
|
|
val t = System.currentTimeMillis()
|
|
|
logger.debug("start merging chunks for [$filename], now: $t")
|
|
|
|
|
|
- val destPath = getFullDestFilepath(destRootPath, filename, relativePath, fileMetadata.repath, fileMetadata.rename)
|
|
|
- val destDir = Paths.get(destPath)
|
|
|
- if (Files.notExists(destDir)) {
|
|
|
- Files.createDirectories(destDir)
|
|
|
- }
|
|
|
// 目标文件流 通过文件名来拼接
|
|
|
- val outputPath = getFullDestFilename(destRootPath, filename, relativePath, fileMetadata.repath, fileMetadata.rename)
|
|
|
- val outputFile = File(outputPath)
|
|
|
+ val outputFullFilename = getFullDestFilename(destRootPath, filename, md5)
|
|
|
+ val outputPath = Paths.get(FilenameUtils.getFullPath(outputFullFilename))
|
|
|
+ // 不存在则创建文件夹
|
|
|
+ if (Files.notExists(outputPath)) {
|
|
|
+ Files.createDirectories(outputPath)
|
|
|
+ }
|
|
|
+ val outputFile = File(outputFullFilename)
|
|
|
val destOutputStream = BufferedOutputStream(FileOutputStream(outputFile))
|
|
|
|
|
|
val buffer = ByteArray(4096)
|
|
@@ -246,52 +264,29 @@ object UploadUtil {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 获取 分片文件名
|
|
|
+ * 生成 分片文件名
|
|
|
*/
|
|
|
private fun getChunkFilename(path: String, chunkNumber: Int?, identifier: String?): String {
|
|
|
return "$path/upload-$identifier.$chunkNumber"
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 生成 输出的文件名
|
|
|
- */
|
|
|
- private fun getDestFileName(filename: String, rename: String?): String {
|
|
|
- return if (rename == null || rename.isEmpty()) {
|
|
|
- filename
|
|
|
- } else {
|
|
|
- // 后缀
|
|
|
- val ext = FilenameUtils.getExtension(rename)
|
|
|
- if (ext.isNullOrEmpty()) {
|
|
|
- "$rename.${FilenameUtils.getExtension(filename)}"
|
|
|
- } else {
|
|
|
- rename
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 生成 输出的文件夹结构
|
|
|
+ * 生成 输出文件名
|
|
|
*/
|
|
|
- private fun getDestFilePath(filename: String, relativePath: String, repath: String?): String {
|
|
|
- return if (repath.isNullOrEmpty()) {
|
|
|
- relativePath.replace(filename, "")
|
|
|
+ private fun getDestFileName(filename: String, md5: String): String {
|
|
|
+ val ext = FilenameUtils.getExtension(filename)
|
|
|
+ return if (!ext.isNullOrEmpty()) {
|
|
|
+ "$md5.$ext"
|
|
|
} else {
|
|
|
- repath!!
|
|
|
+ md5
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 获取输出完整文件路径
|
|
|
- */
|
|
|
- private fun getFullDestFilepath(dataPath: String, filename: String, relativePath: String, repath: String?, rename: String?): String {
|
|
|
- return FilenameUtils.getFullPath(getFullDestFilename(dataPath, filename, relativePath, repath, rename))
|
|
|
- }
|
|
|
-
|
|
|
/**
|
|
|
* 获取输出完整文件名
|
|
|
*/
|
|
|
- private fun getFullDestFilename(dataPath: String, filename: String, relativePath: String, repath: String?, rename: String?): String {
|
|
|
- return FilenameUtils.normalize("$dataPath$FILE_SEPARATOR${getDestFilePath(filename, relativePath, repath)}$FILE_SEPARATOR${getDestFileName(filename, rename)}")
|
|
|
+ private fun getFullDestFilename(dataPath: String, filename: String, md5: String): String {
|
|
|
+ return FilenameUtils.normalize("$dataPath$FILE_SEPARATOR${getDestFileName(filename, md5)}")
|
|
|
}
|
|
|
|
|
|
/**
|