TPath игнорирует регистр при обращении к файлу [Java TrueZip]
Есть ли способ получить доступ к файлу внутри архива, игнорируя регистр имени файла, используя TrueZip?
Представьте себе следующий zip-архив с контентом:
MyZip.zip
-> myFolder/tExtFile.txt
-> anotherFolder/TextFiles/file.txt
-> myFile.txt
-> anotherFile.txt
-> OneMOREfile.txt
Вот как это работает:
TPath tPath = new TPath("MyZip.zip\\myFolder\\tExtFile.txt");
System.out.println(tPath.toFile().getName()); //prints tExtFile.txt
Как сделать то же самое, но игнорировать все дела, например:
// note "myFolder" changed to "myfolder" and "tExtFile" to "textfile"
TPath tPath = new TPath("MyZip.zip\\myfolder\\textfile.txt");
System.out.println(tPath.toFile().getName()); // should print tExtFile.txt
Код выше бросает FsEntryNotFoundException ... (no such entry)
Это работает для регулярного java.io.File
, не уверен, почему не для TFile
TrueZip или я что-то упустил?
Моя цель - получить доступ к каждому файлу, используя только строчные буквы для файлов и папок.
Изменить: 24-03-2017
Допустим, я хотел бы прочитать байты из файла внутри упомянутого zip-архива MyZip.zip
Path tPath = new TPath("...MyZip.zip\\myFolder\\tExtFile.txt");
byte[] bytes = Files.readAllBytes(tPath); //returns bytes of the file
Этот фрагмент кода выше работает, но ниже - нет (выбрасывает упомянутое -> FsEntryNotFoundException
). Это тот же путь и файл только в нижнем регистре.
Path tPath = new TPath("...myzip.zip\\myfolder\\textfile.txt");
byte[] bytes = Files.readAllBytes(tPath);
2 ответа
Вы сказали:
Моя цель - получить доступ к каждому файлу, используя только строчные буквы для файлов и папок.
Но желаемое за действительное не приведет вас сюда слишком далеко. На самом деле, большинство файловых систем (кроме типов Windows) чувствительны к регистру, то есть в них имеет большое значение, если вы используете символы верхнего и нижнего регистра. Там вы даже можете иметь "одно и то же" имя файла в разных случаях несколько раз в одном и том же каталоге. Т.е. это действительно имеет значение, если имя file.txt
, File.txt
или же file.TXT
, Windows на самом деле здесь исключение, но TrueZIP не эмулирует файловую систему Windows, а является общей файловой системой архива, которая работает для ZIP, TAR и т. Д. На всех платформах. Таким образом, у вас нет выбора, использовать ли символы верхнего или нижнего регистра, но вы должны использовать их в точности так, как они хранятся в ZIP-архиве.
Обновление: просто как небольшое доказательство, я вошел в удаленную систему Linux с файловой системой extfs и сделал это:
~$ mkdir test
~$ cd test
~/test$ touch file.txt
~/test$ touch File.txt
~/test$ touch File.TXT
~/test$ ls -l
total 0
-rw-r--r-- 1 group user 0 Mar 25 00:14 File.TXT
-rw-r--r-- 1 group user 0 Mar 25 00:14 File.txt
-rw-r--r-- 1 group user 0 Mar 25 00:14 file.txt
Как вы можете ясно видеть, существует три отдельных файла, а не один.
И что произойдет, если вы заархивируете эти три файла в архив?
~/test$ zip ../files.zip *
adding: File.TXT (stored 0%)
adding: File.txt (stored 0%)
adding: file.txt (stored 0%)
Добавлено три файла. Но они все еще различают файлы в архиве или просто хранятся под одним именем?
~/test$ unzip -l ../files.zip
Archive: ../files.zip
Length Date Time Name
--------- ---------- ----- ----
0 2017-03-25 00:14 File.TXT
0 2017-03-25 00:14 File.txt
0 2017-03-25 00:14 file.txt
--------- -------
0 3 files
"3 файла", - говорится в сообщении.
Как видите, Windows - это не весь мир. Но если вы скопируете этот архив в коробку с Windows и разархивируете его там, он запишет только один файл на диск с файловой системой NTFS или FAT - что является вопросом удачи. Очень плохо, если три файла имеют разное содержимое.
Обновление 2: Хорошо, в TrueZIP нет решения по причинам, подробно описанным выше, но если вы хотите обойти это, вы можете сделать это вручную следующим образом:
package de.scrum_master.app;
import de.schlichtherle.truezip.nio.file.TPath;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Files;
public class Application {
public static void main(String[] args) throws IOException, URISyntaxException {
TPathHelper tPathHelper = new TPathHelper(
new TPath(
"../../../downloads/powershellarsenal-master.zip/" +
"PowerShellArsenal-master\\LIB/CAPSTONE\\LIB\\X64\\LIBCAPSTONE.DLL"
)
);
TPath caseSensitivePath = tPathHelper.getCaseSensitivePath();
System.out.printf("Original path: %s%n", tPathHelper.getOriginalPath());
System.out.printf("Case-sensitive path: %s%n", caseSensitivePath);
System.out.printf("File size: %,d bytes%n", Files.readAllBytes(caseSensitivePath).length);
}
}
package de.scrum_master.app;
import de.schlichtherle.truezip.file.TFile;
import de.schlichtherle.truezip.nio.file.TPath;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Path;
public class TPathHelper {
private final TPath originalPath;
private TPath caseSensitivePath;
public TPathHelper(TPath tPath) {
originalPath = tPath;
}
public TPath getOriginalPath() {
return originalPath;
}
public TPath getCaseSensitivePath() throws IOException, URISyntaxException {
if (caseSensitivePath != null)
return caseSensitivePath;
final TPath absolutePath = new TPath(originalPath.toFile().getCanonicalPath());
TPath matchingPath = absolutePath.getRoot();
for (Path subPath : absolutePath) {
boolean matchFound = false;
for (TFile candidateFile : matchingPath.toFile().listFiles()) {
if (candidateFile.getName().equalsIgnoreCase(subPath.toString())) {
matchFound = true;
matchingPath = new TPath(matchingPath.toString(), candidateFile.getName());
break;
}
}
if (!matchFound)
throw new IOException("element '" + subPath + "' not found in '" + matchingPath + "'");
}
caseSensitivePath = matchingPath;
return caseSensitivePath;
}
}
Конечно, это немного уродливо и просто даст вам первый подходящий путь, если в архиве есть несколько нечувствительных к регистру совпадений. Алгоритм прекратит поиск после первого совпадения в каждом подкаталоге. Я не особенно горжусь этим решением, но это было хорошее упражнение, и вы, кажется, настаиваете, что хотите сделать это таким образом. Я просто надеюсь, что вы никогда не сталкивались с ZIP-архивом в стиле UNIX, созданным в чувствительной к регистру файловой системе и содержащим несколько возможных совпадений.
Кстати, консольный журнал для моего примера файла выглядит так:
Original path: ..\..\..\downloads\powershellarsenal-master.zip\PowerShellArsenal-master\LIB\CAPSTONE\LIB\X64\LIBCAPSTONE.DLL
Case-sensitive path: C:\Users\Alexander\Downloads\PowerShellArsenal-master.zip\PowerShellArsenal-master\Lib\Capstone\lib\x64\libcapstone.dll
File size: 3.629.294 bytes
У меня не установлен TrueZip, но мне также было интересно, как он будет работать в обычном режиме. Path
Таким образом, я реализовал ниже очень похожее решение @kriegaex, вы можете попробовать использовать caseCheck(path)
:
public class Main {
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
Path path = Paths.get("/home/user/workspace/JParser/myfolder/yourfolder/Hisfolder/a.txt");
Instant start = Instant.now();
Path resolution;
try{
resolution = caseCheck(path);
}catch (Exception e) {
throw new IllegalArgumentException("Couldnt access given path", e);
}
Instant end = Instant.now();
Duration duration = Duration.between(start, end);
System.out.println("Path is: " + resolution + " process took " + duration.toMillis() + "ms");
}
/**
* @param path
* @return
* @throws IOException
*/
private static Path caseCheck(Path path) throws IOException {
Path entryPoint = path.isAbsolute() ? path.getRoot() : Paths.get(".");
AtomicInteger counter = new AtomicInteger(0);
while (counter.get() < path.getNameCount()) {
entryPoint = Files
.walk(entryPoint, 1)
.filter(s -> checkPath(s, path, counter.get()))
.findFirst()
.orElseThrow(()->new IllegalArgumentException("No folder found"));
counter.getAndIncrement();
}
return entryPoint;
}
/**
* @param s
* @param path
* @param index
* @return
*/
private static final boolean checkPath(Path s, Path path, int index){
if (s.getFileName() == null) {
return false;
}
return s.getFileName().toString().equalsIgnoreCase(path.getName(index).toString());
}
}