Browse Source

添加上传到本地(非文件服)接口。

NorthLan 6 years ago
parent
commit
cee93c067b

+ 52 - 0
zen-core/src/main/kotlin/com/gxzc/zen/common/dto/UploadFileInfo.kt

@@ -0,0 +1,52 @@
+package com.gxzc.zen.common.dto
+
+import java.io.Serializable
+import java.util.*
+
+/**
+ * 上传的文件信息(本地)
+ * @author NorthLan
+ * @date 2018/7/3
+ * @url https://noahlan.com
+ */
+open class UploadFileInfo : Serializable {
+    companion object {
+        private const val serialVersionUID = 100000000000002L
+    }
+
+    /**
+     * 当前虚拟文件名
+     */
+    var filename: String? = null
+    /**
+     * 真实文件名
+     */
+    var realFilename: String? = null
+    /**
+     * 原始文件名
+     */
+    var originFilename: String? = null
+    /**
+     * 虚拟相对路径
+     */
+    var relativePath: String? = null
+    /**
+     * 最后一次修改时间
+     */
+    var lastModified: Date? = null
+    /**
+     * 文件非完整md5(头+[尾]+修改时间)
+     */
+    var md5: String? = null
+    /**
+     * 后缀名
+     */
+    var extension: String? = null
+    /**
+     * 文件大小(byte)
+     */
+    var filesize: Long? = null
+
+    ////////////////////// url
+    var url: String? = null
+}

+ 81 - 0
zen-web/src/main/kotlin/com/gxzc/zen/web/sys/controller/UploadController.kt

@@ -0,0 +1,81 @@
+package com.gxzc.zen.web.sys.controller
+
+import com.gxzc.zen.common.base.BaseController
+import com.gxzc.zen.common.dto.ZenFileMetadata
+import com.gxzc.zen.common.exception.ZenException
+import com.gxzc.zen.common.util.UploadUtil
+import com.gxzc.zen.web.sys.util.UploadCacheUtil
+import io.swagger.annotations.ApiOperation
+import org.slf4j.LoggerFactory
+import org.springframework.http.HttpStatus
+import org.springframework.http.ResponseEntity
+import org.springframework.web.bind.annotation.GetMapping
+import org.springframework.web.bind.annotation.PostMapping
+import org.springframework.web.bind.annotation.RequestMapping
+import org.springframework.web.bind.annotation.RestController
+import org.springframework.web.multipart.MultipartFile
+
+/**
+ * 上传文件 控制器
+ * @author NorthLan
+ * @date 2018/5/19
+ * @url https://noahlan.com
+ */
+@RestController
+@RequestMapping("/upload")
+class UploadController : BaseController() {
+    companion object {
+        private val logger = LoggerFactory.getLogger(UploadController::class.java)
+    }
+
+    @ApiOperation("获取已上传分片列表")
+    @GetMapping
+    fun checkChunk(fileMetadata: ZenFileMetadata): ResponseEntity<*> {
+        // 检查已上传文件分片
+        val ret = UploadUtil.checkUpload(fileMetadata)
+        return if (ret.uploadedChunks != null) {
+            when (ret.status) {
+            // 单文件完成,插入数据库
+                UploadUtil.STATUS.UPLOADED -> UploadCacheUtil.addUploadFileInfo(fileMetadata, ret.file!!)
+            // 批次完成,将最后个文件插入 而后取出所有本批次文件信息
+                UploadUtil.STATUS.BATCH_UPLOADED -> {
+                    UploadCacheUtil.addUploadFileInfo(fileMetadata, ret.file!!)
+                    ret.info = UploadCacheUtil.getUploadInfosByBatchId(fileMetadata.batchId!!)
+                    UploadCacheUtil.evictCache(fileMetadata.batchId!!)
+                }
+            }
+            ResponseEntity.ok(ret)
+        } else {
+            ResponseEntity.status(204).body(ret)
+        }
+    }
+
+    @ApiOperation("上传", notes = "支持小文件上传,大文件分片上传(统一分片)")
+    @PostMapping
+    fun upload(fileMetadata: ZenFileMetadata, file: MultipartFile?): ResponseEntity<*> {
+        if (file == null) {
+            return ResponseEntity.status(HttpStatus.NOT_ACCEPTABLE.value()).body(null)
+        }
+        val uploadResponse = try {
+            UploadUtil.upload(fileMetadata, file)
+        } catch (e: ZenException) {
+            null
+        }
+
+        return if (uploadResponse != null) {
+            when (uploadResponse.status) {
+            // 单文件完成,插入数据库
+                UploadUtil.STATUS.UPLOADED -> UploadCacheUtil.addUploadFileInfo(fileMetadata, uploadResponse.file!!)
+            // 批次完成,将最后个文件插入 而后取出所有本批次文件信息
+                UploadUtil.STATUS.BATCH_UPLOADED -> {
+                    UploadCacheUtil.addUploadFileInfo(fileMetadata, uploadResponse.file!!)
+                    uploadResponse.info = UploadCacheUtil.getUploadInfosByBatchId(fileMetadata.batchId!!)
+                    UploadCacheUtil.evictCache(fileMetadata.batchId!!)
+                }
+            }
+            ResponseEntity.ok(uploadResponse)
+        } else {
+            ResponseEntity.status(HttpStatus.NOT_ACCEPTABLE.value()).body(null)
+        }
+    }
+}

+ 81 - 0
zen-web/src/main/kotlin/com/gxzc/zen/web/sys/util/UploadCacheUtil.kt

@@ -0,0 +1,81 @@
+package com.gxzc.zen.web.sys.util
+
+import com.github.benmanes.caffeine.cache.Caffeine
+import com.gxzc.zen.common.dto.UploadFileInfo
+import com.gxzc.zen.common.dto.ZenFileMetadata
+import org.apache.commons.io.FilenameUtils
+import org.springframework.cache.caffeine.CaffeineCache
+import java.io.File
+import java.util.*
+import java.util.concurrent.TimeUnit
+
+/**
+ *
+ * @author NorthLan
+ * @date 2018/7/3
+ * @url https://noahlan.com
+ */
+@Suppress("UNCHECKED_CAST")
+object UploadCacheUtil {
+    private val batchFileMap = CaffeineCache("batchFileMap", Caffeine.newBuilder().expireAfterWrite(1L, TimeUnit.HOURS).build(), true) // 文件批量上传批次实际文件信息
+
+    private fun getBatchFileList(batchId: String): MutableList<UploadFileInfo>? {
+        return batchFileMap[batchId]?.get() as? MutableList<UploadFileInfo>
+    }
+
+    private fun addFileInfo(fileInfo: UploadFileInfo, batchId: String) {
+        var fileList = getBatchFileList(batchId)
+        if (fileList == null) {
+            fileList = mutableListOf()
+            batchFileMap.put(batchId, fileList)
+        }
+        fileList.add(fileInfo)
+    }
+
+    /**
+     * 添加文件数据
+     */
+    fun addUploadFileInfo(fileMetadata: ZenFileMetadata, file: File) {
+        val entity = UploadFileInfo().apply {
+            // filename
+            this.filename = if (fileMetadata.rename.isNullOrEmpty()) {
+                fileMetadata.filename
+            } else {
+                "${FilenameUtils.removeExtension(fileMetadata.rename)}.${FilenameUtils.getExtension(fileMetadata.filename)}"
+            }
+            this.realFilename = file.name
+            this.originFilename = fileMetadata.filename
+            // infomation
+            this.relativePath = if (fileMetadata.repath.isNullOrEmpty()) fileMetadata.relativePath?.replace(this.originFilename!!, "") else fileMetadata.repath
+            this.lastModified = Date(file.lastModified())
+            this.md5 = fileMetadata.md5
+            this.extension = FilenameUtils.getExtension(file.name)
+            this.filesize = fileMetadata.totalSize
+        }
+        addFileInfo(entity, fileMetadata.batchId!!)
+    }
+
+    /**
+     * 获取某上传批次所有文件数据
+     */
+    fun getUploadInfosByBatchId(batchId: String): MutableList<UploadFileInfo> {
+        val ret = getBatchFileList(batchId) ?: mutableListOf()
+        ret.forEach {
+            generateURL(it)
+        }
+        return ret
+    }
+
+    /**
+     * 清理缓存
+     */
+    fun evictCache(batchId: String) {
+        batchFileMap.evict(batchId)
+    }
+
+    private fun generateURL(it: UploadFileInfo?) {
+        if (it != null) {
+            it.url = "/${it.realFilename}"
+        }
+    }
+}