From 76e0dcd77a50210f1437fb324073e71028d01a0a Mon Sep 17 00:00:00 2001 From: Dmitry Isaenko Date: Fri, 19 Jan 2024 21:41:11 +0300 Subject: [PATCH] Add zip files repack tool --- .../redrise/marinesco/library/RepackZip.java | 135 ++++++++++++++++++ .../settings/SettingsController.java | 33 ++++- src/main/resources/templates/settings.html | 19 ++- 3 files changed, 181 insertions(+), 6 deletions(-) create mode 100644 src/main/java/ru/redrise/marinesco/library/RepackZip.java diff --git a/src/main/java/ru/redrise/marinesco/library/RepackZip.java b/src/main/java/ru/redrise/marinesco/library/RepackZip.java new file mode 100644 index 0000000..2d6ebf5 --- /dev/null +++ b/src/main/java/ru/redrise/marinesco/library/RepackZip.java @@ -0,0 +1,135 @@ +package ru.redrise.marinesco.library; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.List; +import java.util.stream.Stream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; +import java.util.zip.ZipOutputStream; + +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.stereotype.Component; + +import lombok.extern.slf4j.Slf4j; +import ru.redrise.marinesco.settings.ApplicationSettings; + +/** + * Opens zip file with book files in them, creates folder with name of zip file, + * extracts every book file and put it into individual zip archive + */ +@Slf4j +@Component +public class RepackZip { + private final ThreadPoolTaskExecutor executor; + private final File folderContainer; + private String lastTimeExec = ""; + + + public RepackZip(ThreadPoolTaskExecutor executor, ApplicationSettings applicationSettings) { + this.executor = executor; + this.folderContainer = new File(applicationSettings.getFilesLocation()); + } + + public boolean repack() { + if (executor.getActiveCount() > 0) + return false; + + lastTimeExec = LocalDateTime.now().format(DateTimeFormatter.ofPattern("DD.MM.YYYY HH:mm:ss")); + /* + * if (lastTimeExec != null) + return true; + */ + Thread repackThread = new Thread(() -> { + List files = Stream.of(folderContainer.listFiles()) + .filter(file -> file.getName().toLowerCase().endsWith(".zip")) + .toList(); + + for (File file : files) + executor.execute(new InnerRepackZip(file)); + }); + + repackThread.start(); + + return true; + } + + private class InnerRepackZip implements Runnable { + private String dirLocation; + private File container; + + private InnerRepackZip(File container) { + this.container = container; + } + + @Override + public void run() { + try (ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(container))) { + dirLocation = container.getParentFile().getAbsolutePath() + + File.separator + + container.getName().replaceAll("(\\.zip)|(\\.ZIP)", ""); + + new File(dirLocation).mkdirs(); + + if (!new File(dirLocation).exists()) + throw new Exception("Dir does not created / exists"); + + ZipEntry zipEntry; + + while ((zipEntry = zipInputStream.getNextEntry()) != null) { + if (!zipEntry.isDirectory()) + makeZip(zipEntry, zipInputStream); + } + + } catch (Exception e) { + log.error("{}", e); + } + } + + private void makeZip(ZipEntry entry, InputStream inputStream) throws Exception { + final String newZipFileLocation = dirLocation + File.separator + entry.getName() + ".zip"; + final File newZipFile = new File(dirLocation); + + if (newZipFile.exists()) { + log.info("Skipping unpacked: {}", newZipFileLocation); + return; + } + + try (ZipOutputStream outputStream = new ZipOutputStream( + new FileOutputStream(newZipFile))) { + ZipEntry entryToCompress = new ZipEntry(entry.getName()); + outputStream.putNextEntry(entryToCompress); + + long fileSize = entry.getSize(); + + int blockSize = 2048; + + if (fileSize < 2048) + blockSize = (int) fileSize; + + byte[] block = new byte[blockSize]; + long i = 0; + + while (true) { + int actuallyRead = inputStream.read(block); + outputStream.write(block, 0, actuallyRead); + i += actuallyRead; + if ((i + blockSize) > fileSize) { + blockSize = (int) (fileSize - i); + if (blockSize == 0) + break; + block = new byte[blockSize]; + } + } + } + } + } + + public String getLastExecTime(){ + return lastTimeExec; + } +} \ No newline at end of file diff --git a/src/main/java/ru/redrise/marinesco/settings/SettingsController.java b/src/main/java/ru/redrise/marinesco/settings/SettingsController.java index 7f23383..23fc993 100644 --- a/src/main/java/ru/redrise/marinesco/settings/SettingsController.java +++ b/src/main/java/ru/redrise/marinesco/settings/SettingsController.java @@ -10,6 +10,7 @@ import org.springframework.web.servlet.mvc.support.RedirectAttributes; import org.springframework.web.servlet.view.RedirectView; import ru.redrise.marinesco.library.InpxScanner; +import ru.redrise.marinesco.library.RepackZip; @Controller @RequestMapping("/settings") @@ -18,15 +19,20 @@ public class SettingsController { private ApplicationSettings applicationSettings; private InpxScanner inpxScanner; + private RepackZip repackZip; public SettingsController(ApplicationSettings applicationSettings, - InpxScanner inpxScanner) { + InpxScanner inpxScanner, + RepackZip repackZip) { this.applicationSettings = applicationSettings; this.inpxScanner = inpxScanner; + this.repackZip = repackZip; } @GetMapping - public String getPage(@ModelAttribute("rescanError") String err, @ModelAttribute("rescanOk") String passStatus) { + public String getPage(@ModelAttribute("rescanError") String err, + @ModelAttribute("rescanOk") String passStatus, + @ModelAttribute("repack") String repackStatus) { return "settings"; } @@ -34,11 +40,11 @@ public class SettingsController { public Boolean setRegistrationSetting() { return applicationSettings.isRegistrationAllowed(); } - + @ModelAttribute(name = "lastScanErrors") - public String setLastRunErrors(){ + public String setLastRunErrors() { if (InpxScanner.getLastRunErrors() != "") - return "Last run attempt failed: "+InpxScanner.getLastRunErrors(); + return "Last run attempt failed: " + InpxScanner.getLastRunErrors(); return null; } @@ -60,4 +66,21 @@ public class SettingsController { return redirectView; } + + @GetMapping("/repack") + public RedirectView repack(RedirectAttributes redirectAttributes) { + if (repackZip.repack()) + redirectAttributes.addAttribute("repack", "Repack process started."); + else + redirectAttributes.addAttribute("repack", "Repack process could be currently in progress"); + + return new RedirectView("/settings", true); + } + + @ModelAttribute(name = "repack_lastrun") + public String setLastRunRepackErrors() { + if (repackZip.getLastExecTime() != "") + return "Last time executed: " + repackZip.getLastExecTime(); + return null; + } } diff --git a/src/main/resources/templates/settings.html b/src/main/resources/templates/settings.html index 4a45323..eeb9105 100644 --- a/src/main/resources/templates/settings.html +++ b/src/main/resources/templates/settings.html @@ -20,11 +20,18 @@
Manage users
+

Click to rescan library
+
+ + +
+ Click to repack library zip archives +

Edit genre descriptions @@ -33,4 +40,14 @@
- \ No newline at end of file + + + + + + + Marinesco - Application settings + + + + \ No newline at end of file