From c38b8767a5caf86bb624a77991a8e93e385cdc19 Mon Sep 17 00:00:00 2001 From: Sambo Chea Date: Thu, 21 Apr 2022 16:53:00 +0700 Subject: [PATCH] Add file storage unzip for uploader and extract the resources --- .../web/modules/file/FileStorageFactory.kt | 11 ++- .../modules/file/FileStorageLocalProvider.kt | 7 +- .../web/modules/file/FileStorageUnzip.kt | 7 ++ ...FileStorageZipper.kt => FileStorageZip.kt} | 2 +- .../cubetiqs/web/modules/file/FileZipper.kt | 77 ++++++++++++++++++- .../modules/uploader/UploaderController.kt | 12 ++- 6 files changed, 107 insertions(+), 9 deletions(-) create mode 100644 api/src/main/kotlin/com/cubetiqs/web/modules/file/FileStorageUnzip.kt rename api/src/main/kotlin/com/cubetiqs/web/modules/file/{FileStorageZipper.kt => FileStorageZip.kt} (91%) diff --git a/api/src/main/kotlin/com/cubetiqs/web/modules/file/FileStorageFactory.kt b/api/src/main/kotlin/com/cubetiqs/web/modules/file/FileStorageFactory.kt index eb13e18..d8f87e8 100644 --- a/api/src/main/kotlin/com/cubetiqs/web/modules/file/FileStorageFactory.kt +++ b/api/src/main/kotlin/com/cubetiqs/web/modules/file/FileStorageFactory.kt @@ -2,6 +2,7 @@ package com.cubetiqs.web.modules.file import org.springframework.web.multipart.MultipartFile import java.io.File +import java.io.InputStream object FileStorageFactory { private var provider: FileStorageProvider? = null @@ -36,10 +37,16 @@ object FileStorageFactory { } fun zipAll(): ByteArray? { - if (getProvider() is FileStorageZipper) { - return (getProvider() as FileStorageZipper).zip(null) + if (getProvider() is FileStorageZip) { + return (getProvider() as FileStorageZip).zip(null) } return null } + + fun unzip(inputStream: InputStream) { + if (getProvider() is FileStorageUnzip) { + (getProvider() as FileStorageUnzip).unzip(inputStream, null) + } + } } \ No newline at end of file diff --git a/api/src/main/kotlin/com/cubetiqs/web/modules/file/FileStorageLocalProvider.kt b/api/src/main/kotlin/com/cubetiqs/web/modules/file/FileStorageLocalProvider.kt index 34a88fd..d1ea102 100644 --- a/api/src/main/kotlin/com/cubetiqs/web/modules/file/FileStorageLocalProvider.kt +++ b/api/src/main/kotlin/com/cubetiqs/web/modules/file/FileStorageLocalProvider.kt @@ -2,11 +2,12 @@ package com.cubetiqs.web.modules.file import java.io.File import java.io.FileNotFoundException +import java.io.InputStream import java.io.OutputStream open class FileStorageLocalProvider( private val basePath: String, -) : FileStorageProvider, FileStorageZipper { +) : FileStorageProvider, FileStorageZip, FileStorageUnzip { private fun loadBasePath(fileName: String): String { val prefixPath = if (basePath.endsWith("/")) { "" @@ -60,4 +61,8 @@ open class FileStorageLocalProvider( override fun zip(sourceFolder: String?): ByteArray { return FileZipper.zipToBytes(sourceFolder ?: basePath) } + + override fun unzip(inputStream: InputStream, destinationFolder: String?) { + return FileZipper.unzip(inputStream, destinationFolder ?: basePath) + } } \ No newline at end of file diff --git a/api/src/main/kotlin/com/cubetiqs/web/modules/file/FileStorageUnzip.kt b/api/src/main/kotlin/com/cubetiqs/web/modules/file/FileStorageUnzip.kt new file mode 100644 index 0000000..5943dd7 --- /dev/null +++ b/api/src/main/kotlin/com/cubetiqs/web/modules/file/FileStorageUnzip.kt @@ -0,0 +1,7 @@ +package com.cubetiqs.web.modules.file + +import java.io.InputStream + +interface FileStorageUnzip { + fun unzip(inputStream: InputStream, destinationFolder: String?) +} \ No newline at end of file diff --git a/api/src/main/kotlin/com/cubetiqs/web/modules/file/FileStorageZipper.kt b/api/src/main/kotlin/com/cubetiqs/web/modules/file/FileStorageZip.kt similarity index 91% rename from api/src/main/kotlin/com/cubetiqs/web/modules/file/FileStorageZipper.kt rename to api/src/main/kotlin/com/cubetiqs/web/modules/file/FileStorageZip.kt index 3f51987..4e2c09f 100644 --- a/api/src/main/kotlin/com/cubetiqs/web/modules/file/FileStorageZipper.kt +++ b/api/src/main/kotlin/com/cubetiqs/web/modules/file/FileStorageZip.kt @@ -3,7 +3,7 @@ package com.cubetiqs.web.modules.file import java.io.ByteArrayOutputStream import java.io.OutputStream -interface FileStorageZipper { +interface FileStorageZip { fun zip(sourceFolder: String?, os: OutputStream) fun zip(sourceFolder: String?): ByteArray { val os = ByteArrayOutputStream() diff --git a/api/src/main/kotlin/com/cubetiqs/web/modules/file/FileZipper.kt b/api/src/main/kotlin/com/cubetiqs/web/modules/file/FileZipper.kt index dee9668..6367e63 100644 --- a/api/src/main/kotlin/com/cubetiqs/web/modules/file/FileZipper.kt +++ b/api/src/main/kotlin/com/cubetiqs/web/modules/file/FileZipper.kt @@ -1,15 +1,17 @@ package com.cubetiqs.web.modules.file -import java.io.ByteArrayOutputStream -import java.io.FileOutputStream -import java.io.IOException -import java.io.OutputStream +import java.io.* import java.nio.file.* import java.nio.file.attribute.BasicFileAttributes import java.util.zip.ZipEntry +import java.util.zip.ZipFile +import java.util.zip.ZipInputStream import java.util.zip.ZipOutputStream + object FileZipper { + private const val BUFFER = 2048 + fun zip(sourceFolder: String, destFolder: String) { try { val zipFolder = if (destFolder.endsWith(".zip")) { @@ -55,4 +57,71 @@ object FileZipper { zipToStream(sourceFolder, bos) return bos.toByteArray() } + + fun unzipFromFile(sourceZipFile: File, destFolder: String) { + try { + val zipFile = ZipFile(sourceZipFile) + val entries = zipFile.entries() + while (entries.hasMoreElements()) { + val entry = entries.nextElement() + val destFile = File(destFolder, entry.name) + if (entry.isDirectory) { + destFile.mkdirs() + } else { + destFile.parentFile.mkdirs() + val istream = zipFile.getInputStream(entry) + val ostream = FileOutputStream(destFile) + val buffer = ByteArray(BUFFER) + var len: Int + while (istream.read(buffer).also { len = it } > 0) { + ostream.write(buffer, 0, len) + } + ostream.close() + istream.close() + } + } + zipFile.close() + } catch (e: IOException) { + e.printStackTrace() + } + } + + fun unzip(fis: InputStream, destFolder: String) { + try { + val root = File(destFolder) + if (!root.exists()) { + root.mkdir() + } + val zis = ZipInputStream(BufferedInputStream(fis)) + var entry: ZipEntry? + while (zis.nextEntry.also { entry = it } != null) { + val fileName = entry?.name + val file = File(destFolder + File.separator.toString() + fileName) + if (entry?.isDirectory == false) { + extractFileContentFromArchive(file, zis) + } else { + if (!file.exists()) { + file.mkdirs() + } + } + zis.closeEntry() + } + zis.close() + } catch (e: Exception) { + e.printStackTrace() + } + } + + @Throws(IOException::class) + private fun extractFileContentFromArchive(file: File, zis: ZipInputStream) { + val fos = FileOutputStream(file) + val bos = BufferedOutputStream(fos, BUFFER) + var len: Int + val data = ByteArray(BUFFER) + while (zis.read(data, 0, BUFFER).also { len = it } != -1) { + bos.write(data, 0, len) + } + bos.flush() + bos.close() + } } \ No newline at end of file diff --git a/api/src/main/kotlin/com/cubetiqs/web/modules/uploader/UploaderController.kt b/api/src/main/kotlin/com/cubetiqs/web/modules/uploader/UploaderController.kt index 7a84a16..3a9599c 100644 --- a/api/src/main/kotlin/com/cubetiqs/web/modules/uploader/UploaderController.kt +++ b/api/src/main/kotlin/com/cubetiqs/web/modules/uploader/UploaderController.kt @@ -79,10 +79,11 @@ class UploaderController @Autowired constructor( @GetMapping("/zip", produces = [MediaType.APPLICATION_OCTET_STREAM_VALUE]) @Operation(summary = "Zip all files") fun zipAll( + @RequestParam(required = false, value = "filename", defaultValue = "files") filename: String?, response: HttpServletResponse, ) { val zipBytes = FileStorageFactory.zipAll() ?: throw IllegalArgumentException("Zip file not found") - response.setHeader("Content-Disposition", "attachment; filename=\"files.zip\"") + response.setHeader("Content-Disposition", "attachment; filename=\"$filename.zip\"") response.contentType = "application/zip" response.setContentLengthLong(zipBytes.size.toLong()) @@ -99,6 +100,15 @@ class UploaderController @Autowired constructor( return repository.save(entity) } + @ResponseStatus(value = org.springframework.http.HttpStatus.CREATED) + @PostMapping("/unzip", consumes = [MediaType.MULTIPART_FORM_DATA_VALUE]) + @Operation(summary = "Zip a file") + fun unzip( + @RequestPart file: MultipartFile, + ) { + FileStorageFactory.unzip(file.inputStream) + } + @ResponseStatus(value = org.springframework.http.HttpStatus.OK) @PutMapping("/{id}") @Operation(summary = "Update a file by id")