Add zip files repack tool
This commit is contained in:
parent
57de3bea2f
commit
76e0dcd77a
3 changed files with 181 additions and 6 deletions
135
src/main/java/ru/redrise/marinesco/library/RepackZip.java
Normal file
135
src/main/java/ru/redrise/marinesco/library/RepackZip.java
Normal 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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
@ -33,4 +40,14 @@
|
|||
<div th:replace="~{fragments/footer :: 'footer'}"></div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</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>
|
Loading…
Reference in a new issue