better archive support

This commit is contained in:
Radek Davidek 2026-05-14 18:55:46 +02:00
parent c2865750c0
commit a4dedd1324
2 changed files with 173 additions and 14 deletions

View File

@ -735,7 +735,7 @@ public class FileOperations {
public static byte[] readFileFromArchive(File archive, String entryPath) throws IOException { public static byte[] readFileFromArchive(File archive, String entryPath) throws IOException {
String name = archive.getName().toLowerCase(); String name = archive.getName().toLowerCase();
try { try {
if (name.endsWith(".zip") || name.endsWith(".jar")) { if (name.endsWith(".car") || name.endsWith(".zip") || name.endsWith(".jar")) {
try (ZipInputStream zis = new ZipInputStream(new FileInputStream(archive))) { try (ZipInputStream zis = new ZipInputStream(new FileInputStream(archive))) {
ZipEntry entry; ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) { while ((entry = zis.getNextEntry()) != null) {
@ -817,14 +817,138 @@ public class FileOperations {
public static boolean isArchiveFile(String filename) { public static boolean isArchiveFile(String filename) {
if (filename == null) return false; if (filename == null) return false;
String n = filename.toLowerCase(); String n = filename.toLowerCase();
return n.endsWith(".war") || n.endsWith(".zip") || n.endsWith(".jar") || n.endsWith(".tar") || n.endsWith(".tar.gz") || n.endsWith(".tgz") || n.endsWith(".7z") || n.endsWith(".rar"); return n.endsWith(".car") || n.endsWith(".war") || n.endsWith(".zip") || n.endsWith(".jar") || n.endsWith(".tar") || n.endsWith(".tar.gz") || n.endsWith(".tgz") || n.endsWith(".7z") || n.endsWith(".rar");
}
/**
* Checks if a file can be opened as an archive by actually attempting to read it.
* This method does NOT rely on file extension - it always tries to actually
* read and parse the file as an archive to verify its contents.
*/
public static boolean canOpenAsArchive(File file) {
if (file == null || !file.exists() || !file.isFile()) {
return false;
}
// Always try to detect format by actually reading the file content
// This works for any file regardless of extension
return tryDetectArchiveFormat(file);
}
/**
* Attempts to detect the archive format by trying to read the file.
* Returns true if the file can be opened as any supported archive format.
*/
private static boolean tryDetectArchiveFormat(File file) {
try {
String name = file.getName().toLowerCase();
// Try ZIP (includes .car, .zip, .jar, .war, and any file with zip magic)
if (name.endsWith(".car") || name.endsWith(".zip") || name.endsWith(".jar") || name.endsWith(".war")) {
try (ZipInputStream zis = new ZipInputStream(new FileInputStream(file))) {
ZipEntry entry = zis.getNextEntry();
if (entry != null) {
return true;
}
} catch (Exception ignore) {
// Not a valid zip
}
} else {
// Try ZIP for unknown extensions (zip files without extension)
try (ZipInputStream zis = new ZipInputStream(new FileInputStream(file))) {
ZipEntry entry = zis.getNextEntry();
if (entry != null) {
return true;
}
} catch (Exception ignore) {
// Not a valid zip
}
}
// Try 7z
if (name.endsWith(".7z")) {
try (SevenZFile szf = new SevenZFile(file)) {
SevenZArchiveEntry entry = szf.getNextEntry();
if (entry != null) {
return true;
}
} catch (Exception ignore) {
// Not a valid 7z
}
} else {
// Try 7z for unknown extensions
try (SevenZFile szf = new SevenZFile(file)) {
SevenZArchiveEntry entry = szf.getNextEntry();
if (entry != null) {
return true;
}
} catch (Exception ignore) {
// Not a valid 7z
}
}
// Try RAR
if (name.endsWith(".rar")) {
try (com.github.junrar.Archive rar = new com.github.junrar.Archive(file)) {
if (rar.getFileHeaders().size() > 0) {
return true;
}
} catch (Exception ignore) {
// Not a valid rar
}
} else {
// Try RAR for unknown extensions
try (com.github.junrar.Archive rar = new com.github.junrar.Archive(file)) {
if (rar.getFileHeaders().size() > 0) {
return true;
}
} catch (Exception ignore) {
// Not a valid rar
}
}
// Try TAR/TAR.GZ
if (name.endsWith(".tar") || name.endsWith(".tar.gz") || name.endsWith(".tgz")) {
try {
InputStream is = new FileInputStream(file);
if (name.endsWith(".gz") || name.endsWith(".tgz")) {
is = new org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream(is);
}
try (org.apache.commons.compress.archivers.tar.TarArchiveInputStream tais = new org.apache.commons.compress.archivers.tar.TarArchiveInputStream(is)) {
org.apache.commons.compress.archivers.tar.TarArchiveEntry entry = tais.getNextTarEntry();
if (entry != null) {
return true;
}
}
} catch (Exception ignore) {
// Not a valid tar
}
} else {
// Try TAR for unknown extensions
try {
InputStream is = new FileInputStream(file);
try (org.apache.commons.compress.archivers.tar.TarArchiveInputStream tais = new org.apache.commons.compress.archivers.tar.TarArchiveInputStream(is)) {
org.apache.commons.compress.archivers.tar.TarArchiveEntry entry = tais.getNextTarEntry();
if (entry != null) {
return true;
}
}
} catch (Exception ignore) {
// Not a valid tar
}
}
return false;
} catch (Exception e) {
return false;
}
} }
private static void searchInArchiveCombined(File archive, String patternLower, Pattern filenameRegex, Pattern contentPattern, boolean caseSensitive, SearchCallback callback) { private static void searchInArchiveCombined(File archive, String patternLower, Pattern filenameRegex, Pattern contentPattern, boolean caseSensitive, SearchCallback callback) {
if (callback != null && callback.isCancelled()) return; if (callback != null && callback.isCancelled()) return;
String name = archive.getName().toLowerCase(); String name = archive.getName().toLowerCase();
try { try {
if (name.endsWith(".zip") || name.endsWith(".jar") || name.endsWith(".war")) { if (name.endsWith(".car") || name.endsWith(".zip") || name.endsWith(".jar") || name.endsWith(".war")) {
try (ZipInputStream zis = new ZipInputStream(new FileInputStream(archive))) { try (ZipInputStream zis = new ZipInputStream(new FileInputStream(archive))) {
ZipEntry entry; ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) { while ((entry = zis.getNextEntry()) != null) {
@ -1037,7 +1161,7 @@ public class FileOperations {
String name = archive.getName().toLowerCase(); String name = archive.getName().toLowerCase();
try { try {
if (name.endsWith(".zip") || name.endsWith(".jar") || name.endsWith(".war")) { if (name.endsWith(".zip") || name.endsWith(".jar") || name.endsWith(".war") || name.endsWith(".car")) {
extractZip(archive, targetDir, callback); extractZip(archive, targetDir, callback);
} else if (name.endsWith(".7z")) { } else if (name.endsWith(".7z")) {
extract7z(archive, targetDir, callback); extract7z(archive, targetDir, callback);
@ -1046,7 +1170,40 @@ public class FileOperations {
} else if (name.endsWith(".tar") || name.endsWith(".tar.gz") || name.endsWith(".tgz")) { } else if (name.endsWith(".tar") || name.endsWith(".tar.gz") || name.endsWith(".tgz")) {
extractTar(archive, targetDir, callback); extractTar(archive, targetDir, callback);
} else { } else {
throw new IOException("Unsupported archive format: " + name); // For unknown extensions, try to detect format by attempting to read
// First try ZIP (most common)
try {
extractZip(archive, targetDir, callback);
return; // Success
} catch (IOException zipEx) {
// Not a zip, try other formats
}
// Try 7z
try {
extract7z(archive, targetDir, callback);
return; // Success
} catch (IOException sevenZEx) {
// Not a 7z
}
// Try RAR
try {
extractRar(archive, targetDir, callback);
return; // Success
} catch (IOException rarEx) {
// Not a rar
}
// Try TAR/TAR.GZ
try {
extractTar(archive, targetDir, callback);
return; // Success
} catch (IOException tarEx) {
// Not a tar
}
throw new IOException("Unsupported or invalid archive format: " + name);
} }
} catch (Exception e) { } catch (Exception e) {
if (callback != null) { if (callback != null) {
@ -1066,7 +1223,7 @@ public class FileOperations {
} }
String name = archive.getName().toLowerCase(); String name = archive.getName().toLowerCase();
if (!(name.endsWith(".zip") || name.endsWith(".jar") || name.endsWith(".war"))) { if (!(name.endsWith(".car") || name.endsWith(".zip") || name.endsWith(".jar") || name.endsWith(".war"))) {
throw new IOException("Unsupported archive format for in-memory zip extraction: " + archive.getName()); throw new IOException("Unsupported archive format for in-memory zip extraction: " + archive.getName());
} }
@ -1282,7 +1439,7 @@ public class FileOperations {
public static boolean supportsArchiveRewrite(File archive) { public static boolean supportsArchiveRewrite(File archive) {
if (archive == null) return false; if (archive == null) return false;
String name = archive.getName().toLowerCase(); String name = archive.getName().toLowerCase();
return name.endsWith(".zip") || name.endsWith(".jar") || name.endsWith(".war"); return name.endsWith(".car") || name.endsWith(".zip") || name.endsWith(".jar") || name.endsWith(".war");
} }
public static void rewriteArchiveFromDirectory(File sourceDir, File targetArchive, String password, ProgressCallback callback) throws IOException { public static void rewriteArchiveFromDirectory(File sourceDir, File targetArchive, String password, ProgressCallback callback) throws IOException {

View File

@ -1162,10 +1162,12 @@ public class FilePanelTab extends JPanel {
int row = fileTable.getSelectedRow(); int row = fileTable.getSelectedRow();
if (row >= 0) { if (row >= 0) {
FileItem item = (viewMode == ViewMode.BRIEF) ? tableModel.getItemFromBriefLayout(row, briefCurrentColumn) : tableModel.getItem(row); FileItem item = (viewMode == ViewMode.BRIEF) ? tableModel.getItemFromBriefLayout(row, briefCurrentColumn) : tableModel.getItem(row);
if (item != null && (item.isDirectory() || FileOperations.isArchiveFile(item.getFile()))) { if (item != null && !item.isFtp()) {
if (item.isDirectory() || (item.getFile() != null && FileOperations.canOpenAsArchive(item.getFile()))) {
openSelectedItem(); openSelectedItem();
} }
} }
}
e.consume(); e.consume();
} else if (e.getKeyCode() == java.awt.event.KeyEvent.VK_PAGE_UP && e.isControlDown()) { } else if (e.getKeyCode() == java.awt.event.KeyEvent.VK_PAGE_UP && e.isControlDown()) {
navigateUp(); navigateUp();
@ -2063,7 +2065,7 @@ public class FilePanelTab extends JPanel {
if (item.getName().equals("..")) { if (item.getName().equals("..")) {
navigateUp(); navigateUp();
} else if (FileOperations.isArchiveFile(item.getFile())) { } else if (FileOperations.canOpenAsArchive(item.getFile())) {
rememberArchiveReturnState(item.getFile()); rememberArchiveReturnState(item.getFile());
final File archiveFile = item.getFile(); final File archiveFile = item.getFile();
@ -2097,7 +2099,7 @@ public class FilePanelTab extends JPanel {
); );
} else if (item.isDirectory()) { } else if (item.isDirectory()) {
loadDirectory(item.getFile()); loadDirectory(item.getFile());
} else if (item.getFile().isFile()) { } else if (item.getFile() != null && item.getFile().isFile()) {
openFileNative(item.getFile()); openFileNative(item.getFile());
} }
} }
@ -2610,7 +2612,7 @@ public class FilePanelTab extends JPanel {
if (item.getName().equals("..")) { if (item.getName().equals("..")) {
navigateUp(); navigateUp();
} else if (FileOperations.isArchiveFile(item.getFile())) { } else if (FileOperations.canOpenAsArchive(item.getFile())) {
rememberArchiveReturnState(item.getFile()); rememberArchiveReturnState(item.getFile());
final File archiveFile = item.getFile(); final File archiveFile = item.getFile();
@ -2634,7 +2636,7 @@ public class FilePanelTab extends JPanel {
); );
} else if (item.isDirectory()) { } else if (item.isDirectory()) {
loadDirectory(item.getFile()); loadDirectory(item.getFile());
} else if (item.getFile().isFile()) { } else if (item.getFile() != null && item.getFile().isFile()) {
openFileNative(item.getFile()); openFileNative(item.getFile());
} }
} }
@ -4882,7 +4884,7 @@ public class FilePanelTab extends JPanel {
} }
}; };
if (archiveLower.endsWith(".zip") || archiveLower.endsWith(".jar") || archiveLower.endsWith(".war")) { if (archiveLower.endsWith(".car") || archiveLower.endsWith(".zip") || archiveLower.endsWith(".jar") || archiveLower.endsWith(".war")) {
FileOperations.extractZipArchiveFromMemory(archive, tempDir.toFile(), callback); FileOperations.extractZipArchiveFromMemory(archive, tempDir.toFile(), callback);
} else { } else {
FileOperations.extractArchive(archive, tempDir.toFile(), callback); FileOperations.extractArchive(archive, tempDir.toFile(), callback);