Как извлечь один файл из удаленного архивного файла?

Учитывая

  • URL-адрес архива (например, zip файл)
  • Полное имя (включая путь) файла внутри этого архива

Я ищу способ (желательно на Java) создать локальную копию этого файла без первой загрузки всего архива.

Из моего (ограниченного) понимания это должно быть возможно, хотя я понятия не имею, как это сделать. Я использовал TrueZip, поскольку, похоже, он поддерживает большое количество типов архивов, но у меня есть сомнения в его способности работать в таким образом. Кто-нибудь имеет опыт такого рода вещей?

РЕДАКТИРОВАТЬ: возможность также делать это с tarballs и zipped tarballs также важна для меня.

Ответ 1

Ну, как минимум, вам нужно загрузить часть архива до и включить сжатые данные файла, который вы хотите извлечь. Это предлагает следующее решение: откройте URLConnection в архиве, получите его поток ввода, оберните его в ZipInputStream и повторно вызовите getNextEntry() и closeEntry(), чтобы перебирать все записи в файле, пока не достигнете тот, который вы хотите. Затем вы можете прочитать свои данные с помощью ZipInputStream.read(...).

Код Java выглядит примерно так:

URL url = new URL("http://example.com/path/to/archive");
ZipInputStream zin = new ZipInputStream(url.getInputStream());
ZipEntry ze = zin.getNextEntry();
while (!ze.getName().equals(pathToFile)) {
    zin.closeEntry(); // not sure whether this is necessary
    ze = zin.getNextEntry();
}
byte[] bytes = new byte[ze.getSize()];
zin.read(bytes);

Это, конечно, непроверено.

Ответ 2

В отличие от других ответов здесь, я хотел бы указать, что записи ZIP сжимаются индивидуально, поэтому (в теории) вам не нужно загружать ничего больше, чем каталог и сама запись. Для этого сервер должен поддерживать HTTP-заголовок Range.

Стандартный Java API поддерживает только чтение ZIP файлов из локальных файлов и потоков ввода. Насколько я знаю, нет никаких условий для чтения из удаленных файлов с произвольным доступом.

Поскольку вы используете TrueZip, я рекомендую внедрить de.schlichtherle.io.rof.ReadOnlyFile с помощью Apache HTTP Client и создать с ним de.schlichtherle.util.zip.ZipFile.

Это не даст никаких преимуществ для сжатых архивов TAR, поскольку весь архив сжимается вместе (помимо использования InputStream и его убийства, когда у вас есть запись).

Ответ 3

С TrueZIP 7.2 в модуле TrueZIP Path появился новый клиентский API. Это реализация NIO.2 FileSystemProvider для JSE 7. Используя этот API, вы можете получить доступ к URI HTTP следующим образом:

Path path = new TPath(new URI("http://acme.com/download/everything.tar.gz/README.TXT"));
try (InputStream in = Files.newInputStream(path)) {
    // Read archive entry contents here.
    ...
}

Ответ 4

Я не уверен, есть ли способ вытащить один файл из ZIP без первой загрузки всего. Но если вы являетесь владельцем ZIP файла, вы можете создать сервлет Java, который читает ZIP файл и возвращает запрошенный файл в ответ:

public class GetFileFromZIPServlet extends HttpServlet{
  @Override
  public void doGet(HttpServletRequest request, HttpServletResponse response)
  throws ServletException, IOException{
    String pathToFile = request.getParameter("pathToFile");

    byte fileBytes[];
    //get the bytes of the file from the ZIP

    //set the appropriate content type, maybe based on the file extension
    response.setContentType("...");

    //write file to the response
    response.getOutputStream().write(fileBytes);
  }
}