java.util.zip-odtworzenie struktury katalogów
Podczas próby spakowania archiwum za pomocą java.util.zip
natknąłem się na wiele problemów, z których większość rozwiązałem. Teraz, gdy w końcu mam jakieś wyjście, zmagam się z uzyskaniem "właściwego" wyjścia. Mam wyodrębniony plik ODT (katalog byłby bardziej pasujący do opisu), do którego zrobiłem kilka modyfikacji. Teraz chcę skompresować ten katalog, aby odtworzyć strukturę plików ODT. Zamykanie katalogu i zmiana jego nazwy na koniec .odt działa dobrze, więc nie powinno być problemu.
Główny problem polega na tym, że tracę wewnętrzną strukturę katalogu. Wszystko staje się "płaskie" i wydaje mi się, że nie znajduję sposobu na zachowanie oryginalnej wielowarstwowej struktury. Byłbym wdzięczny za pomoc w tej sprawie, ponieważ nie mogę znaleźć problemu.
Oto odpowiednie fragmenty kodu:
ZipOutputStream out = new ZipOutputStream(new FileOutputStream(
FILEPATH.substring(0, FILEPATH.lastIndexOf(SEPARATOR) + 1).concat("test.zip")));
compressDirectory(TEMPARCH, out);
SEPARATOR
jest separatorem plików systemowych, a {[4] } jest ścieżką plików oryginalnego ODT, którą nadpisuję, ale nie zrobiłem tego tutaj w celach testowych. Po prostu piszę na test.zip plik w tym samym katalogu.
private void compressDirectory(String directory, ZipOutputStream out) throws IOException
{
File fileToCompress = new File(directory);
// list contents.
String[] contents = fileToCompress.list();
// iterate through directory and compress files.
for(int i = 0; i < contents.length; i++)
{
File f = new File(directory, contents[i]);
// testing type. directories and files have to be treated separately.
if(f.isDirectory())
{
// add empty directory
out.putNextEntry(new ZipEntry(f.getName() + SEPARATOR));
// initiate recursive call
compressDirectory(f.getPath(), out);
// continue the iteration
continue;
}else{
// prepare stream to read file.
FileInputStream in = new FileInputStream(f);
// create ZipEntry and add to outputting stream.
out.putNextEntry(new ZipEntry(f.getName()));
// write the data.
int len;
while((len = in.read(data)) > 0)
{
out.write(data, 0, len);
}
out.flush();
out.closeEntry();
in.close();
}
}
}
Katalog zawierający pliki do zip znajduje się gdzieś w przestrzeni użytkownika, a nie w tym samym katalogu co plik wynikowy. Zakładam, że to może być problem, ale nie wiem jak. Również pomyślałem, że problem może być w użyciu tego samego strumienia do wyprowadzania, ale znowu nie widzę, jak. Widziałem w kilku przykładach i tutorialach, że używają getPath()
zamiast getName()
, ale zmiana daje mi pusty plik zip.
8 answers
Klasa URI jest przydatna do pracy ze ścieżkami względnymi.
File mydir = new File("C:\\mydir");
File myfile = new File("C:\\mydir\\path\\myfile.txt");
System.out.println(mydir.toURI().relativize(myfile.toURI()).getPath());
Powyższy kod będzie emitował łańcuch path/myfile.txt
.
Dla kompletności, oto zip
metoda archiwizacji katalogu:
public static void zip(File directory, File zipfile) throws IOException {
URI base = directory.toURI();
Deque<File> queue = new LinkedList<File>();
queue.push(directory);
OutputStream out = new FileOutputStream(zipfile);
Closeable res = out;
try {
ZipOutputStream zout = new ZipOutputStream(out);
res = zout;
while (!queue.isEmpty()) {
directory = queue.pop();
for (File kid : directory.listFiles()) {
String name = base.relativize(kid.toURI()).getPath();
if (kid.isDirectory()) {
queue.push(kid);
name = name.endsWith("/") ? name : name + "/";
zout.putNextEntry(new ZipEntry(name));
} else {
zout.putNextEntry(new ZipEntry(name));
copy(kid, zout);
zout.closeEntry();
}
}
}
} finally {
res.close();
}
}
Ten kod nie zachowuje dat i nie jestem pewien, jak zareagowałby na takie rzeczy jak dowiązania symboliczne. Nie jest podejmowana próba dodania wpisów katalogowych, więc puste katalogi nie będą dołączane.
Odpowiednie polecenie unzip
:
public static void unzip(File zipfile, File directory) throws IOException {
ZipFile zfile = new ZipFile(zipfile);
Enumeration<? extends ZipEntry> entries = zfile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
File file = new File(directory, entry.getName());
if (entry.isDirectory()) {
file.mkdirs();
} else {
file.getParentFile().mkdirs();
InputStream in = zfile.getInputStream(entry);
try {
copy(in, file);
} finally {
in.close();
}
}
}
}
Użyteczność metody, na których się opierają:
private static void copy(InputStream in, OutputStream out) throws IOException {
byte[] buffer = new byte[1024];
while (true) {
int readCount = in.read(buffer);
if (readCount < 0) {
break;
}
out.write(buffer, 0, readCount);
}
}
private static void copy(File file, OutputStream out) throws IOException {
InputStream in = new FileInputStream(file);
try {
copy(in, out);
} finally {
in.close();
}
}
private static void copy(InputStream in, File file) throws IOException {
OutputStream out = new FileOutputStream(file);
try {
copy(in, out);
} finally {
out.close();
}
}
Rozmiar bufora jest całkowicie dowolny.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-09 13:52:52
Widzę 2 problemy w Twoim kodzie,
- nie zapisujesz ścieżki katalogu, więc nie ma sposobu, aby ją odzyskać.
- w systemie Windows musisz użyć "/" jako separatora ścieżek. Jakiś program rozpakowujący nie lubi \.
Dołączam własną wersję w celach informacyjnych. Używamy tego do zapinania zdjęć do pobrania, dzięki czemu działa z różnymi programami rozpakowującymi. Zachowuje strukturę katalogów i znaczniki czasu.
public static void createZipFile(File srcDir, OutputStream out,
boolean verbose) throws IOException {
List<String> fileList = listDirectory(srcDir);
ZipOutputStream zout = new ZipOutputStream(out);
zout.setLevel(9);
zout.setComment("Zipper v1.2");
for (String fileName : fileList) {
File file = new File(srcDir.getParent(), fileName);
if (verbose)
System.out.println(" adding: " + fileName);
// Zip always use / as separator
String zipName = fileName;
if (File.separatorChar != '/')
zipName = fileName.replace(File.separatorChar, '/');
ZipEntry ze;
if (file.isFile()) {
ze = new ZipEntry(zipName);
ze.setTime(file.lastModified());
zout.putNextEntry(ze);
FileInputStream fin = new FileInputStream(file);
byte[] buffer = new byte[4096];
for (int n; (n = fin.read(buffer)) > 0;)
zout.write(buffer, 0, n);
fin.close();
} else {
ze = new ZipEntry(zipName + '/');
ze.setTime(file.lastModified());
zout.putNextEntry(ze);
}
}
zout.close();
}
public static List<String> listDirectory(File directory)
throws IOException {
Stack<String> stack = new Stack<String>();
List<String> list = new ArrayList<String>();
// If it's a file, just return itself
if (directory.isFile()) {
if (directory.canRead())
list.add(directory.getName());
return list;
}
// Traverse the directory in width-first manner, no-recursively
String root = directory.getParent();
stack.push(directory.getName());
while (!stack.empty()) {
String current = (String) stack.pop();
File curDir = new File(root, current);
String[] fileList = curDir.list();
if (fileList != null) {
for (String entry : fileList) {
File f = new File(curDir, entry);
if (f.isFile()) {
if (f.canRead()) {
list.add(current + File.separator + entry);
} else {
System.err.println("File " + f.getPath()
+ " is unreadable");
throw new IOException("Can't read file: "
+ f.getPath());
}
} else if (f.isDirectory()) {
list.add(current + File.separator + entry);
stack.push(current + File.separator + f.getName());
} else {
throw new IOException("Unknown entry: " + f.getPath());
}
}
}
}
return list;
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-09 20:57:07
Wystarczy przejrzeć źródło Javy.util.zip.ZipEntry. Traktuje zipentry jako katalog, jeśli jego nazwa kończy się znakami"/". Wystarczy przyrostek nazwy katalogu z "/". Musisz również usunąć prefiks dysku, aby uczynić go względnym.
Sprawdź ten przykład dla zipping tylko puste katalogi,
Tak długo, jak jesteś w stanie utworzyć zarówno puste & niepuste katalogi w pliku ZIP, struktura katalogów jest nienaruszona.
Powodzenia.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-25 07:37:14
Oto kolejny przykład (rekurencyjny), który pozwala również na dołączenie / wykluczenie folderu zawierającego z zip:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class ZipUtil {
private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
public static void main(String[] args) throws Exception {
zipFile("C:/tmp/demo", "C:/tmp/demo.zip", true);
}
public static void zipFile(String fileToZip, String zipFile, boolean excludeContainingFolder)
throws IOException {
ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(zipFile));
File srcFile = new File(fileToZip);
if(excludeContainingFolder && srcFile.isDirectory()) {
for(String fileName : srcFile.list()) {
addToZip("", fileToZip + "/" + fileName, zipOut);
}
} else {
addToZip("", fileToZip, zipOut);
}
zipOut.flush();
zipOut.close();
System.out.println("Successfully created " + zipFile);
}
private static void addToZip(String path, String srcFile, ZipOutputStream zipOut)
throws IOException {
File file = new File(srcFile);
String filePath = "".equals(path) ? file.getName() : path + "/" + file.getName();
if (file.isDirectory()) {
for (String fileName : file.list()) {
addToZip(filePath, srcFile + "/" + fileName, zipOut);
}
} else {
zipOut.putNextEntry(new ZipEntry(filePath));
FileInputStream in = new FileInputStream(srcFile);
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
int len;
while ((len = in.read(buffer)) != -1) {
zipOut.write(buffer, 0, len);
}
in.close();
}
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-14 00:45:03
Jeśli nie chcesz zajmować się bajtowymi strumieniami wejściowymi, rozmiarami buforów i innymi niskopoziomowymi szczegółami. Możesz użyć biblioteki Zip Ant z kodu java(zależności maven można znaleźć tutaj ). Oto teraz robię zip składający się z listy plików i katalogów:
public static void createZip(File zipFile, List<String> fileList) {
Project project = new Project();
project.init();
Zip zip = new Zip();
zip.setDestFile(zipFile);
zip.setProject(project);
for(String relativePath : fileList) {
//noramalize the path (using commons-io, might want to null-check)
String normalizedPath = FilenameUtils.normalize(relativePath);
//create the file that will be used
File fileToZip = new File(normalizedPath);
if(fileToZip.isDirectory()) {
ZipFileSet fileSet = new ZipFileSet();
fileSet.setDir(fileToZip);
fileSet.setPrefix(fileToZip.getPath());
zip.addFileset(fileSet);
} else {
FileSet fileSet = new FileSet();
fileSet.setDir(new File("."));
fileSet.setIncludes(normalizedPath);
zip.addFileset(fileSet);
}
}
Target target = new Target();
target.setName("ziptarget");
target.addTask(zip);
project.addTarget(target);
project.executeTarget("ziptarget");
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-24 20:47:21
Do zip zawartości folderu i jego podfolderów w systemie Windows,
Zastąp,
out.putNextEntry(new ZipEntry(files[i]));
Z
out.putNextEntry(new ZipEntry(files[i]).replace(inFolder+"\\,""));
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-27 09:24:59
Chciałbym dodać tutaj sugestię/przypomnienie:
Jeśli zdefiniujesz katalog wyjściowy taki sam jak katalog wejściowy, będziesz chciał porównać nazwę KAŻDEGO pliku z wyjściem .nazwa pliku zip, w celu uniknięcia kompresji pliku wewnątrz siebie, generowania niechcianych zachowań. Mam nadzieję, że to pomoże.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-06 06:45:00
Ten wycięty kod działa dla mnie. Nie jest potrzebna biblioteka innych firm.
public static void zipDir(final Path dirToZip, final Path out) {
final Stack<String> stackOfDirs = new Stack<>();
final Function<Stack<String>, String> createPath = stack -> stack.stream().collect(Collectors.joining("/")) + "/";
try(final ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(out.toFile()))) {
Files.walkFileTree(dirToZip, new FileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attrs) throws IOException {
stackOfDirs.push(dir.toFile().getName());
final String path = createPath.apply(stackOfDirs);
final ZipEntry zipEntry = new ZipEntry(path);
zipOut.putNextEntry(zipEntry);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
final String path = String.format("%s%s", createPath.apply(stackOfDirs), file.toFile().getName());
final ZipEntry zipEntry = new ZipEntry(path);
zipOut.putNextEntry(zipEntry);
Files.copy(file, zipOut);
zipOut.closeEntry();
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(final Path file, final IOException exc) throws IOException {
final StringWriter stringWriter = new StringWriter();
try(final PrintWriter printWriter = new PrintWriter(stringWriter)) {
exc.printStackTrace(printWriter);
System.err.printf("Failed visiting %s because of:\n %s\n",
file.toFile().getAbsolutePath(), printWriter.toString());
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(final Path dir, final IOException exc) throws IOException {
stackOfDirs.pop();
return FileVisitResult.CONTINUE;
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-16 02:23:08