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 @@