refactor: replace RarExtractor and ZipExtractor with a unified SevenZipExtractor implementation
This commit is contained in:
parent
1ed1d380db
commit
35b4caffb6
12
pom.xml
12
pom.xml
@ -131,10 +131,14 @@
|
|||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.junrar</groupId>
|
<groupId>net.sf.sevenzipjbinding</groupId>
|
||||||
<artifactId>junrar</artifactId>
|
<artifactId>sevenzipjbinding</artifactId>
|
||||||
<version>7.5.8</version>
|
<version>16.02-2.01</version>
|
||||||
<scope>compile</scope>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.sf.sevenzipjbinding</groupId>
|
||||||
|
<artifactId>sevenzipjbinding-all-platforms</artifactId>
|
||||||
|
<version>16.02-2.01</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
|||||||
@ -1,58 +0,0 @@
|
|||||||
package com.magamochi.content.service.archive;
|
|
||||||
|
|
||||||
import static java.util.Objects.isNull;
|
|
||||||
import static java.util.Objects.nonNull;
|
|
||||||
|
|
||||||
import com.github.junrar.Archive;
|
|
||||||
import com.github.junrar.rarfile.FileHeader;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.StandardCopyOption;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.TreeMap;
|
|
||||||
import net.greypanther.natsort.CaseInsensitiveSimpleNaturalComparator;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
@Component
|
|
||||||
public class RarExtractor implements ArchiveExtractor {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean supports(String mimeType) {
|
|
||||||
if (isNull(mimeType)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return mimeType.startsWith("application/x-rar-compressed")
|
|
||||||
|| mimeType.startsWith("application/rar");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<String, byte[]> extract(InputStream stream) throws Exception {
|
|
||||||
Map<String, byte[]> entryMap =
|
|
||||||
new TreeMap<>(CaseInsensitiveSimpleNaturalComparator.getInstance());
|
|
||||||
|
|
||||||
var tempFile = Files.createTempFile("manga_import_rar_", ".rar");
|
|
||||||
|
|
||||||
try {
|
|
||||||
Files.copy(stream, tempFile, StandardCopyOption.REPLACE_EXISTING);
|
|
||||||
|
|
||||||
try (var archive = new Archive(tempFile.toFile())) {
|
|
||||||
FileHeader fileHeader;
|
|
||||||
while (nonNull(fileHeader = archive.nextFileHeader())) {
|
|
||||||
if (fileHeader.isDirectory()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var os = new ByteArrayOutputStream();
|
|
||||||
archive.extractFile(fileHeader, os);
|
|
||||||
entryMap.put(fileHeader.getFileName(), os.toByteArray());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
Files.deleteIfExists(tempFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
return entryMap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,130 @@
|
|||||||
|
package com.magamochi.content.service.archive;
|
||||||
|
|
||||||
|
import static java.util.Objects.isNull;
|
||||||
|
import static java.util.Objects.nonNull;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.RandomAccessFile;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.StandardCopyOption;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import net.greypanther.natsort.CaseInsensitiveSimpleNaturalComparator;
|
||||||
|
import net.sf.sevenzipjbinding.ExtractAskMode;
|
||||||
|
import net.sf.sevenzipjbinding.ExtractOperationResult;
|
||||||
|
import net.sf.sevenzipjbinding.IArchiveExtractCallback;
|
||||||
|
import net.sf.sevenzipjbinding.IInArchive;
|
||||||
|
import net.sf.sevenzipjbinding.ISequentialOutStream;
|
||||||
|
import net.sf.sevenzipjbinding.PropID;
|
||||||
|
import net.sf.sevenzipjbinding.SevenZip;
|
||||||
|
import net.sf.sevenzipjbinding.SevenZipException;
|
||||||
|
import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class SevenZipExtractor implements ArchiveExtractor {
|
||||||
|
public SevenZipExtractor() {
|
||||||
|
try {
|
||||||
|
SevenZip.initSevenZipFromPlatformJAR();
|
||||||
|
log.info("Successfully initialized SevenZipJBinding for multi-platform archive extraction.");
|
||||||
|
} catch (net.sf.sevenzipjbinding.SevenZipNativeInitializationException e) {
|
||||||
|
log.error(
|
||||||
|
"Failed to initialize SevenZipJBinding. Native bindings may be missing or incompatible.",
|
||||||
|
e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supports(String mimeType) {
|
||||||
|
if (isNull(mimeType)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mimeType.startsWith("application/zip")
|
||||||
|
|| mimeType.startsWith("application/x-rar-compressed")
|
||||||
|
|| mimeType.startsWith("application/rar")
|
||||||
|
|| mimeType.startsWith("application/x-7z-compressed");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, byte[]> extract(InputStream stream) throws Exception {
|
||||||
|
Map<String, byte[]> entryMap =
|
||||||
|
new TreeMap<>(CaseInsensitiveSimpleNaturalComparator.getInstance());
|
||||||
|
var tempFile = Files.createTempFile("manga_import_archive_", ".tmp");
|
||||||
|
|
||||||
|
try {
|
||||||
|
Files.copy(stream, tempFile, StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
|
||||||
|
try (var randomAccessFile = new RandomAccessFile(tempFile.toFile(), "r");
|
||||||
|
var inStream = new RandomAccessFileInStream(randomAccessFile);
|
||||||
|
var inArchive = SevenZip.openInArchive(null, inStream)) {
|
||||||
|
|
||||||
|
inArchive.extract(null, false, new ExtractCallback(inArchive, entryMap));
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
Files.deleteIfExists(tempFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
return entryMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ExtractCallback implements IArchiveExtractCallback {
|
||||||
|
private final IInArchive inArchive;
|
||||||
|
private final Map<String, byte[]> entryMap;
|
||||||
|
private int currentIndex;
|
||||||
|
private ByteArrayOutputStream currentOs;
|
||||||
|
|
||||||
|
public ExtractCallback(IInArchive inArchive, Map<String, byte[]> entryMap) {
|
||||||
|
this.inArchive = inArchive;
|
||||||
|
this.entryMap = entryMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ISequentialOutStream getStream(int index, ExtractAskMode extractAskMode)
|
||||||
|
throws SevenZipException {
|
||||||
|
this.currentIndex = index;
|
||||||
|
|
||||||
|
if (!extractAskMode.equals(ExtractAskMode.EXTRACT)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var isFolder = (Boolean) inArchive.getProperty(index, PropID.IS_FOLDER);
|
||||||
|
if (nonNull(isFolder) && isFolder) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentOs = new ByteArrayOutputStream();
|
||||||
|
return data -> {
|
||||||
|
try {
|
||||||
|
currentOs.write(data);
|
||||||
|
return data.length;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new SevenZipException("Error writing to stream", e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void prepareOperation(ExtractAskMode extractAskMode) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setOperationResult(ExtractOperationResult extractOperationResult)
|
||||||
|
throws SevenZipException {
|
||||||
|
if (extractOperationResult.equals(ExtractOperationResult.OK) && nonNull(currentOs)) {
|
||||||
|
var path = (String) inArchive.getProperty(currentIndex, PropID.PATH);
|
||||||
|
entryMap.put(path, currentOs.toByteArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
currentOs = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setTotal(long total) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setCompleted(long complete) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,47 +0,0 @@
|
|||||||
package com.magamochi.content.service.archive;
|
|
||||||
|
|
||||||
import static java.util.Objects.isNull;
|
|
||||||
import static java.util.Objects.nonNull;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.TreeMap;
|
|
||||||
import java.util.zip.ZipEntry;
|
|
||||||
import java.util.zip.ZipInputStream;
|
|
||||||
import net.greypanther.natsort.CaseInsensitiveSimpleNaturalComparator;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
@Component
|
|
||||||
public class ZipExtractor implements ArchiveExtractor {
|
|
||||||
@Override
|
|
||||||
public boolean supports(String mimeType) {
|
|
||||||
if (isNull(mimeType)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return mimeType.startsWith("application/zip");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<String, byte[]> extract(InputStream stream) throws Exception {
|
|
||||||
Map<String, byte[]> entryMap =
|
|
||||||
new TreeMap<>(CaseInsensitiveSimpleNaturalComparator.getInstance());
|
|
||||||
|
|
||||||
try (var zis = new ZipInputStream(stream)) {
|
|
||||||
ZipEntry entry;
|
|
||||||
while (nonNull(entry = zis.getNextEntry())) {
|
|
||||||
if (entry.isDirectory()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var os = new ByteArrayOutputStream();
|
|
||||||
zis.transferTo(os);
|
|
||||||
entryMap.put(entry.getName(), os.toByteArray());
|
|
||||||
zis.closeEntry();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return entryMap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user