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());
    }
}
Другие вопросы по тегам