Add zip files repack tool

This commit is contained in:
Dmitry Isaenko 2024-01-19 21:41:11 +03:00
parent 57de3bea2f
commit 76e0dcd77a
3 changed files with 181 additions and 6 deletions

View file

@ -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<File> 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;
}
}

View file

@ -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";
}
@ -36,9 +42,9 @@ public class SettingsController {
}
@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;
}
}

View file

@ -20,11 +20,18 @@
<br />
<a href="/settings/manage_users">Manage users</a>
<br />
<br />
<span class="validationError" th:if="${rescanError} != null" th:text="${rescanError}"></span>
<span class="validationPass" th:if="${rescanOk} != null" th:text="${rescanOk}"></span>
<br />
<a href="/settings/rescan" >Click to rescan library</a>
<br />
<br />
<span class="validationPass" th:if="${repack} != null" th:text="${repack}"></span>
<span class="validationPass" th:if="${repack_lastrun} != null" th:text="${repack_lastrun}"></span>
<br />
<a href="/settings/repack" >Click to repack library zip archives</a>
<br />
<span class="validationError" th:if="${lastScanErrors} != ''" th:text="${lastScanErrors}"></span>
</p>
<a href="/settings/genres">Edit genre descriptions</a>
@ -34,3 +41,13 @@
</body>
</html>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Marinesco - Application settings</title>
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
<link rel="alternate icon" href="/favicon.png" type="image/png">
<link rel="stylesheet" th:href="@{/styles/styles.css}" />
</head>