I have a zip file containing only files and not directories. I want to unzip the file into a directory by using only functional Java.
The code below works as expected and unzips the file into the target folder.
public static void unzip(Path source, Path target) throws IOException {
try (ZipInputStream zis = new ZipInputStream(new FileInputStream(source.toFile()))) {
ZipEntry zipEntry = zis.getNextEntry();
while (zipEntry != null) {
Path targetDirResolved = target.resolve(zipEntry.getName());
Path normalizePath = targetDirResolved.normalize();
if (!normalizePath.startsWith(target)) {
throw new IOException("Bad zip entry: " + zipEntry.getName());
}
Files.copy(zis, normalizePath, StandardCopyOption.REPLACE_EXISTING);
zipEntry = zis.getNextEntry();
}
zis.closeEntry();
}
}
I want to achieve the same functionality as above but use a more functional approach.
My initial thoughts are to transform the while loop into something like IntStream.range(0, ).... but the number of entries inside the zip file is not known.
Any ideas?
One way to eliminate the while loop is to use ZipFilestream:
try (ZipFile fs = new ZipFile(source.toFile())) {
fs.stream()
.filter(Predicate.not(ZipEntry::isDirectory))
// etc
.forEach(System.out::println);
}
You could also look at ZIP filesystem with Files.find to provide a suitable Stream of ZIP entries (of level one files) and if required, do additional filtering / map or other conversions on the stream instead of forEach as shown here:
try (FileSystem fs = FileSystems.newFileSystem(source)) {
for (Path root : fs.getRootDirectories()) {
try (Stream<Path> stream = Files.find(root, 1, (p,a) -> a.isRegularFile())) {
stream.forEach(p -> copy(p, Path.of(target.toString(), p.toString())));
}
}
}
Unfortunately the above still uses normal loops for root directories and copy is a separate method to handle IOException:
private static void copy(Path from, Path to) {
try {
Files.copy(from, to, StandardCopyOption.REPLACE_EXISTING);
}
catch (IOException e) {
throw new UncheckedIOException(e);
}
}
Related
i have checked everywhere online and stackoverflow and could not find a match specific to this issue.
I am trying to extract a pdf file that is located in a zip file that is inside a zip file (nested zips).
Re-calling the method i am using to extract does not work nor does changing the whole program to accept Inputstreams instead of how i am doing it below.
The .pdf file inside the nested zip is just skipped at this stage
public static void main(String[] args)
{
try
{
//Paths
String basePath = "C:\\Users\\user\\Desktop\\Scan\\";
File lookupDir = new File(basePath + "Data\\");
String doneFolder = basePath + "DoneUnzipping\\";
File[] directoryListing = lookupDir.listFiles();
for (int i = 0; i < directoryListing.length; i++)
{
if (directoryListing[i].isFile()) //there's definately a file
{
//Save the current file's path
String pathOrigFile = directoryListing[i].getAbsolutePath();
Path origFileDone = Paths.get(pathOrigFile);
Path newFileDone = Paths.get(doneFolder + directoryListing[i].getName());
//unzip it
if(directoryListing[i].getName().toUpperCase().endsWith(ZIP_EXTENSION)) //ZIP files
{
unzip(directoryListing[i].getAbsolutePath(), DESTINATION_DIRECTORY + directoryListing[i].getName());
//move to the 'DoneUnzipping' folder
Files.move(origFileDone, newFileDone);
}
}
}
} catch (Exception e)
{
e.printStackTrace(System.out);
}
}
private static void unzip(String zipFilePath, String destDir)
{
//buffer for read and write data to file
byte[] buffer = new byte[BUFFER_SIZE];
try (ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFilePath)))
{
FileInputStream fis = new FileInputStream(zipFilePath);
ZipEntry ze = zis.getNextEntry();
while(ze != null)
{
String fileName = ze.getName();
int index = fileName.lastIndexOf("/");
String newFileName = fileName.substring(index + 1);
File newFile = new File(destDir + File.separator + newFileName);
//Zips inside zips
if(fileName.toUpperCase().endsWith(ZIP_EXTENSION))
{
ZipInputStream innerZip = new ZipInputStream(zis);
ZipEntry innerEntry = null;
while((innerEntry = innerZip.getNextEntry()) != null)
{
System.out.println("The file: " + fileName);
if(fileName.toUpperCase().endsWith("PDF"))
{
FileOutputStream fos = new FileOutputStream(newFile);
int len;
while ((len = innerZip.read(buffer)) > 0)
{
fos.write(buffer, 0, len);
}
fos.close();
}
}
}
//close this ZipEntry
zis.closeEntry(); // java.io.IOException: Stream Closed
ze = zis.getNextEntry();
}
//close last ZipEntry
zis.close();
fis.close();
} catch (IOException e)
{
e.printStackTrace();
}
}
The solution to this is not as obvious as it seems. Despite writing a few zip utilities myself some time ago, getting zip entries from inside another zip file only seems obvious in retrospect
(and I also got the java.io.IOException: Stream Closed on my first attempt).
The Java classes for ZipFile and ZipInputStream really direct your thinking into using the file system, but it is not required.
The functions below will scan a parent-level zip file, and continue scanning until it finds an entry with a specified name. (Nearly) everything is done in-memory.
Naturally, this can be modified to use different search criteria, find multiple file types, etc. and take different actions, but this at least demonstrates the basic technique in question -- zip files inside of zip files -- no guarantees on other aspects of the code, and someone more savvy could most likely improve the style.
final static String ZIP_EXTENSION = ".zip";
public static byte[] getOnePDF() throws IOException
{
final File source = new File("/path/to/MegaData.zip");
final String nameToFind = "FindThisFile.pdf";
final ByteArrayOutputStream mem = new ByteArrayOutputStream();
try (final ZipInputStream in = new ZipInputStream(new BufferedInputStream(new FileInputStream(source))))
{
digIntoContents(in, nameToFind, mem);
}
// Save to disk, if you want
// copy(new ByteArrayInputStream(mem.toByteArray()), new FileOutputStream(new File("/path/to/output.pdf")));
// Otherwise, just return the binary data
return mem.toByteArray();
}
private static void digIntoContents(final ZipInputStream in, final String nameToFind, final ByteArrayOutputStream mem) throws IOException
{
ZipEntry entry;
while (null != (entry = in.getNextEntry()))
{
final String name = entry.getName();
// Found the file we are looking for
if (name.equals(nameToFind))
{
copy(in, mem);
return;
}
// Found another zip file
if (name.toUpperCase().endsWith(ZIP_EXTENSION.toUpperCase()))
{
digIntoContents(new ZipInputStream(new ByteArrayInputStream(getZipEntryFromMemory(in))), nameToFind, mem);
}
}
}
private static byte[] getZipEntryFromMemory(final ZipInputStream in) throws IOException
{
final ByteArrayOutputStream mem = new ByteArrayOutputStream();
copy(in, mem);
return mem.toByteArray();
}
// General purpose, reusable, utility function
// OK for binary data (bad for non-ASCII text, use Reader/Writer instead)
public static void copy(final InputStream from, final OutputStream to) throws IOException
{
final int bufferSize = 4096;
final byte[] buf = new byte[bufferSize];
int len;
while (0 < (len = from.read(buf)))
{
to.write(buf, 0, len);
}
to.flush();
}
Your question asks how to use java (by implication in windows) to extract a pdf from a zip inside another outer zip.
In many systems including windows it is a single line command that will depend on the location of source and target folders, however using the shortest example of current downloads folder it would be in a shell as simple as
tar -xf "german (2).zip" && tar -xf "german.zip" && german.pdf
to shell the command in windows see
How do I execute Windows commands in Java?
The default pdf viewer can open the result so Windows Edge or in my case SumatraPDF
There is generally no point in putting a pdf inside a zip because it cannot be run in there. So single nesting would be advisable if needed for download transportation.
There is no need to add a password to the zip because PDF uses its own password for opening. Thus unwise to add two levels of complexity. Keep it simple.
If you have multiple zips nested inside multiple zips with multiple pdfs in each then you have to be more specific by filtering names. However avoid that extra onion skin where possible.
\Downloads>tar -xf "german (2).zip" "both.zip" && tar -xf "both.zip" "English language.pdf"
You could complicate that by run in a memory or temp folder but it is reliable and simple to use the native file system so consider without Java its fastest to run
CD /D "C:/Users/user/Desktop/Scan/DoneUnzipping" && for %f in (..\Data\*.zip) do tar -xf "%f" "*.zip" && for %f in (*.zip) do tar -xf "%f" "*.pdf" && del "*.zip"
This will extract all inner zips into working folder then extract all PDFs and remove all the essential temporary zips. The source double zips will not be deleted simply touched.
The line that causes your problem looks to be auto-close block you have created when reading the inner zip:
try(ZipInputStream innerZip = new ZipInputStream(fis)) {
...
}
Several likely issues: firstly it is reading the wrong stream - fis not the existing zis.
Secondly, you shouldn't use try-with-resources for auto-close on innerZip as this implicitly calls innerZip.close() when exiting the block. If you view the source code of ZipInputStream via a good IDE you should see (eventually) that ZipInputStream extends InflaterInputStream which itself extends FilterInputStream. A call to innerZip.close() will close the underlying outer stream zis (fis in your case) hence stream is closed when you resume the next entry of the outer zip.
Therefore remove the try() block and add use of zis:
ZipInputStream innerZip = new ZipInputStream(zis);
Use try-catch block only for the outermost file handling:
try (ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFilePath))) {
ZipEntry ze = zis.getNextEntry();
...
}
Thirdly, you appear to be copying the wrong stream when extracting a PDF - use innerZip not outer zis. The code will never extract PDF as these 2 lines can never be true at the same time because a file ending ZIP will never end PDF too:
if(fileName.toUpperCase().endsWith(ZIP_EXTENSION)) {
...
// You want innerEntry.getName() here
if(fileName.toUpperCase().endsWith("PDF"))
You should be able to switch to one line Files.copy and make use of the PDF filename not zip filename:
if(innerEntry.getName().toUpperCase().endsWith("PDF")) {
Path newFile = Paths.get(destDir + '-'+innerEntry.getName().replace("/", "-"));
System.out.println("Files.copy to " + newFile);
Files.copy(innerZip, newFile);
}
I have file in the following structure:
--BA.zip
|
|--- BA (directory)
|
|---BA_KKSSI_20201013.zip
| |
| |---BA_KKSSI_20201013.txt
|---BA_KKSSI_20201014.zip
| |
| |---BA_KKSSI_20201014.txt
|---BA_KKSSI_20201015.zip
|
|---BA_KKSSI_20201015.txt
I need to read BA_KKSSI_20201013.txt without extracting the parent file which is BA.zip
I have already written parts of code to read if there is no sub dirs. For example:
public static String readChildZip(Path zipPath) throws IOException {
try (ZipFile zipFile = new ZipFile(zipPath.toFile())) {
// since there is only one text file
ZipEntry textFile = zipFile.entries().nextElement();
// the zip
System.out.println(zipFile.getName());
InputStream is = zipFile.getInputStream(textFile);
String contents = IOUtils.toString(is, StandardCharsets.UTF_8);
return contents;
}
}
Above code can process the last zip and txt part (i.e., if there are no sub-dirs within a zip)
I looked through most of the SO posts and all of them propose extracting the sub-directory first and then read through the secondary zip files.
Is there a way to do this without extracting in the first place?
You can use ZipInputStream (https://docs.oracle.com/javase/7/docs/api/java/util/zip/ZipInputStream.html) to read entries in the "outer" zip file as zip files as well.
Meaning open the zip file as you have but then iterate over and if a entry is a zipfile itself you create a ZipInputStream with the InputStream for that ZipEntry.
This returns the contents of the first text file inside the first Zip file in zipPath.
public static String readChildZip(Path zipPath) throws IOException {
try (ZipFile zipFile = new ZipFile(zipPath.toFile())) {
ZipEntry childZipEntry = zipFile.entries().nextElement();
try (InputStream childInputStream = zipFile.getInputStream(childZipEntry);
ZipInputStream childZipStream = new ZipInputStream(childInputStream)) {
childZipStream.getNextEntry();
return new String(childZipStream.readAllBytes(), StandardCharsets.UTF_8);
}
}
}
And this will print the contents of all text files inside the first Zip file in zipPath.
public static void readChildZipAll(Path zipPath) throws IOException {
try (ZipFile zipFile = new ZipFile(zipPath.toFile())) {
ZipEntry childZipEntry = zipFile.entries().nextElement();
try (InputStream childInputStream = zipFile.getInputStream(childZipEntry);
ZipInputStream childZipStream = new ZipInputStream(childInputStream)) {
ZipEntry grandChildEntry;
while ((grandChildEntry = childZipStream.getNextEntry()) != null) {
System.out.println(grandChildEntry + ":"
+ new String(childZipStream.readAllBytes(), StandardCharsets.UTF_8));
}
}
}
}
You should look at using NIO ZIP File System or ZipInputStream if wanting to scan a ZIP.
Here is an example of using ZIP File System in a recursive scanner which can be used to inspect any level of depth of JAR/ZIP/WAR/EAR hierarchy. You should adapt to suit own purposes for whatever action you need to perform on the content, this example just cats any ".txt" files to the console.
Note that ZIP File System returns zip filesystem Path objects which can be used with NIO Files.xxx() calls such as Files.find() and Find.copy() just like you would use for Path that originate from default HDD filesystems.
private static Pattern ZIP_PATTERN = Pattern.compile("(?i).*\\.(jar|war|ear|zip)");
public static void traverseZip(Path zip) {
System.out.println("traverseZip "+zip.toAbsolutePath());
try (FileSystem fs = FileSystems.newFileSystem(zip)) {
for (Path root : fs.getRootDirectories()) {
try (Stream<Path> stream = Files.find(root, Integer.MAX_VALUE, (p,a) -> true)) {
stream.forEach(entry -> {
System.out.println(zip.toString()+" -> "+entry);
// SOME ACTION HERE, for example
if (entry.toString().endsWith(".txt")) {
cat(entry, System.out);
}
if (ZIP_PATTERN.matcher(entry.toString()).matches() && Files.isRegularFile(entry)) {
traverseZip(entry);
}
});
}
}
}
catch(IOException e) {
throw new UncheckedIOException(e);
}
}
private static void cat(Path path, OutputStream out) {
try {
Files.copy(path, out);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
Launch with:
traverseZip(Path.of("some.zip"));
I have gone through the link of how to extract a .tar file and several link on SOF using Java.
However, I didnt find any which can relate to my concerns which is multilevel or nested .tar/.tgz/.zip file.
my concern is with something like below
Abc.tar.gz
--DEF.tar
--sample1.txt
--sample2.txt
--FGH.tgz
--sample3.txt
-sample4.txt
This is the simple one which I can give here . As it can be in any compressed combination with the folder like .tar inside .tar and .gz and again .tgz and so on....
My problem is I am able to extract till the first level using Apache Commons Compress library. that is if Abc.tar.gz gets extracted then in the destination/output folder its only DEF.tar available . beyond that my extraction is not working.
I tried to give the output of first to the input to the second on the fly but I got stuck with FileNotFoundException. As at that point of time output file would have not been in place and the second extraction not able to get the file.
Pseudocode:
public class CommonExtraction {
TarArchiveInputStream tar = null;
if((sourcePath.trim().toLowerCase.endsWith(".tar.gz")) || sourcePath.trim().toLowerCase.endsWith(".tgz")) {
try {
tar=new TarArchiveInputStream(new GzipCompressorInputStream(new BufferedInputStream(new FileInputStream(sourcePath))));
extractTar(tar,destPath)
} catch (Exception e) {
e.printStackTrace();
}
}
}
Public static void extractTar(TarArchiveInputStream tar, String outputFolder) {
try{
TarArchiveEntry entry;
while (null!=(entry=(TarArchiveEntry)tar.getNextTarEntry())) {
if(entry.getName().trim().toLowerCase.endsWith(".tar")){
final String path = outputFolder + entry.getName()
tar=new TarArchiveInputStream(new BufferedInputStream(new FileInputStream(path))) // failing as .tar folder after decompression from .gz not available at destination path
extractTar(tar,outputFolder)
}
extractEntry(entry,tar,outputFolder)
}
tar.close();
}catch(Exception ex){
ex.printStackTrace();
}
}
Public static void extractEntry(TarArchiveEntry entry , InputStream tar, String outputFolder){
final String path = outputFolder + entry.getName()
if(entry.isDirectory()){
new File(path).mkdirs();
}else{
//create directory for the file if not exist
}
// code to read and write until last byte is encountered
}
}
Ps: please ignore the syntax and all in the code.
Try this
try (InputStream fi = file.getInputStream();
InputStream bi = new BufferedInputStream(fi);
InputStream gzi = new GzipCompressorInputStream(bi, false);
ArchiveInputStream archive = new TarArchiveInputStream(gzi)) {
withArchiveStream(archive, result::appendEntry);
}
As i see what .tar.gz and .tgz is same formats. And my method withArchiveEntry is:
private void withArchiveStream(ArchiveInputStream archInStream, BiConsumer<ArchiveInputStream, ArchiveEntry> entryConsumer) throws IOException {
ArchiveEntry entry;
while((entry = archInStream.getNextEntry()) != null) {
entryConsumer.accept(archInStream, entry);
}
}
private void appendEntry(ArchiveInputStream archive, ArchiveEntry entry) {
if (!archive.canReadEntryData(entry)) {
throw new IOException("Can`t read archive entry");
}
if (entry.isDirectory()) {
return;
}
// And for example
String content = new String(archive.readAllBytes(), StandardCharsets.UTF_8);
System.out.println(content);
}
You have a recursive problem, so you can use recursion to solve it. Here is some pseudocode to show how it can be done:
public class ArchiveExtractor
{
public void extract(File file)
{
List<File> files; // list of extracted files
if(isZip(file))
files = extractZip(file);
else if(isTGZ(file))
files = extractTGZ(file);
else if(isTar(file))
files = extractTar(file);
else if(isGZip(file))
files = extractGZip(file);
for(File f : files)
{
if(isArchive(f))
extract(f); // recursive call
}
}
private List<File> extractZip(File file)
{
// extract archive and return list of extracted files
}
private List<File> extractTGZ(File file)
{
// extract archive and return list of extracted files
}
private List<File> extractTar(File file)
{
// extract archive and return list of extracted files
}
private List<File> extractGZip(File file)
{
// extract archive and return list of extracted file
}
}
where:
isZip() tests if the file extension is zip
isTGZ() tests if the file extension is tgz
isTar() tests if the file extension is tar
isGZip() tests if the file extension is gz
isArchive() means isZip() || isTGZ() || isTar() || isGZip()
As for the directory where each archive is extracted: you are free to do as you want.
If you process test.zip for example, you may extract in the same directory as where the archive is,
or create the directory test and extract in it.
I want to manipulate a jar using the standard nio Files and Paths methods. So, Java has a way to do this by creating a zip FileSystem:
try {
zipFS = FileSystems.newFileSystem(zipDisk, zipFSproperties);
} catch(Exception e) {
System.out.println(e.getMessage());
}
My test program uses an existing jar file as a FileSystem and it lists the entries contained in the jar. All that works great. I then copy a new file into the jar and list the entries again. And just as you would expect, the list now contains the newly added file. The problem is after the program closes, I open up the jar file that the jar filesystem is based upon and it doesn't have the new entry added to it. So that's my question! Shouldn't the jar file itself be changed when I add a new entry. I don't know of any commands I can issue the would cause the zip FileSystem to update to the actual jar file that the zip FileSystem wraps. Am I reading more into a FileSystem; are changes in the zip filesystem suppose to cause the corresponding backend zip file to be updated.
code:
public class Main {
public static void main(String[] args) throws IOException {
ZipFileSystem zipFS = new ZipFileSystem("C:\\Temp\\mylibrary\\build\\outputs\\jar\\temp\\mylibrary-debug.zip");
Stream<Path> paths = Files.find(zipFS.zipFS.getRootDirectories().iterator().next().getRoot(),10, (path, basicFileAttributes) -> {
return !Files.isDirectory(path);
});
paths.forEach( path ->
System.out.println ("zip contains entry: " + path)
);
File file = new File("C:\\Temp\\mylibrary\\src\\main\\java\\com\\phinneyridge\\android\\myLib.java");
System.out.println("copying " + file.getPath());
Path outPath = zipFS.zipFS.getPath("myLib.java");
Files.copy (file.toPath(), outPath, StandardCopyOption.REPLACE_EXISTING);
paths = Files.find(zipFS.zipFS.getPath(""),10, (path, basicFileAttributes) -> {
return !Files.isDirectory(path);
});
paths.forEach( path ->
System.out.println ("zip contains entry: " + path)
);
}
}
I added code that shows me accessing a zip file, listing the current entries it contains, adding a new entry (via file copy), and lastly listing the contents again. All of this code works correctly. What doesn't work is that the changes to the zip filesystem don't get incorporated back into the zip file when the application ends. I was surprised that the zip file didn't get updated, but I'm now under the opinion, that it's working as it is intended to work; not doing what I wanted it to do, but that's okay. I can't find any documentation that says it would update the jar file that the FileSystem object originated from. So I'm basically asking is that the correct behavior, or is there something I'm entirely missing to cause the zip FileSystem object to update the Zip file?
Here's the code when I tried Dunc suggestion:
public static void main(String[] args) throws IOException {
ZipFileSystem zipFS = new ZipFileSystem("C:\\Temp\\mylibrary\\build\\outputs\\jar\\temp\\mylibrary-debug.zip");
try (FileSystem fs = zipFS.zipFS) {
try (Stream<Path> paths = Files.find(zipFS.zipFS.getRootDirectories().
iterator().next().getRoot(), 10, (path, basicFileAttributes) -> {
return !Files.isDirectory(path);
})) {
paths.forEach(path ->
System.out.println("zip contains entry: " + path)
);
}
File file = new File("C:\\Temp\\mylibrary\\src\\main\\java\\com\\phinneyridge\\android\\myLib.java");
System.out.println("copying " + file.getPath());
Path outPath = fs.getPath("myLib.java");
Files.copy(file.toPath(), outPath, StandardCopyOption.REPLACE_EXISTING);
try (Stream<Path> paths = Files.find(zipFS.zipFS.getRootDirectories().
iterator().next().getRoot(), 10, (path, basicFileAttributes) -> {
return !Files.isDirectory(path);
})) {
paths.forEach(path ->
System.out.println("zip contains entry: " + path)
);
}
} catch (Exception e) {
System.out.println("FileSystem Error: " + e.getClass().getName() + " - " + e.getMessage());
e.printStackTrace();
}
}
}
And by the way ZipFileSystem is a wrapper class around the FileSystem. I'll post that code too, incase that's where I 'm doing something wrong.
public class ZipFileSystem {
FileSystem zipFS;
Path zipFSPath;
/**
* Constructor for a ZipFile object
* #param zipFilePath string representing the path to the zipfile. If the path doesn't exist,
* the zip file will be automatically created. If the path exist, it must be a file (not
* a directory) and it must be a valid zip file
*/
public ZipFileSystem(String zipFilePath) {
Map<String, String> zipFSproperties = new HashMap<>();
/* set create to true if you want to create a new ZIP file */
zipFSproperties.put("create", "true");
/* specify encoding to UTF-8 */
zipFSproperties.put("encoding", "UTF-8");
/* Locate File on disk for creation */
URI zipFileUri = new File(zipFilePath).toURI();
URI zipDisk = URI.create("jar:" + zipFileUri);
zipFSPath = Paths.get(zipFileUri);
if (!Files.exists(zipFSPath)) {
try {
createEmptyZipFile(zipFSPath);
} catch (Exception e ) {
System.out.println(e.getMessage());
}
} else {
if (Files.isDirectory(zipFSPath)) {
} else {
try {
// let's open it, which will verify if it's a valid zip file
ZipFile zipFile = new ZipFile(zipFilePath);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
try {
zipFS = FileSystems.newFileSystem(zipDisk, zipFSproperties);
} catch(Exception e) {
System.out.println(e.getMessage());
}
try {
listFiles(zipFS.getPath("/"));
} catch (IOException e) {
e.printStackTrace();
}
}
The correct way to open a zip from a Path - and create if not exists - is:
Path zip = Path.of("/Somepath/to/xyz.zip");
Map<String, String> env = Map.of(
"create", "true"
// other args here ...
);
try (FileSystem fs = FileSystems.newFileSystem(zip, env)) {
// code to read/update here
}
You have not closed any files or streams properly so your changes are probably not flushed back to the file system and will keep hold of file handles which block some operations.
Use try with resources for every operation which will manage the modifications to zip filesystem as well as closing each Stream<Path> from Files.find, and check other places such as createEmptyZipFile for the same problem:
try (FileSystem fs = ... ) {
try (Stream<Path> paths = Files.find(...) ) {
}
Files.copy( ... );
try (Stream<Path> paths = Files.find(...) ) {
}
}
The process cannot access the file because it is being used by another process
You have unnecessary code ZipFile zipFile = new ZipFile(zipFilePath) which tests the zip is valid and you do not call close(), so it will prevent the zip changes being written back. The check can safely be deleted (as FileSystems.newFileSystem does same) or must be wrapped in try() {} so that zipFile is closed before your edits to the zip filesystem.
I want to extract a zip file which contains a jar file. This file has complex folder structure and in one of the folders there is a jar file. When I am trying to use the following code to extract the jar file the program goes in infinite loop in reading the jar file and never recovers. It keeps on writing the contents of the jar till we reach the limit of the disc space even though the jar is of only a few Mbs.
Please find the code snippet below
`
// using a ZipInputStream to get the zipIn by passing the zipFile as FileInputStream
ZipEntry entry = zipIn.getNextEntry();
String fileName= entry.getName()
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(fileName));
byte[] bytesIn = new byte[(int)bufferSize];
while (zipIn.read(bytesIn) > 0) // This is the part where the loop does not end
{
bos.write(bytesIn);
}
..
// flushing an closing the bos
Please let me know if there is any way we can avoid this and get the jar file out at required location.
Does this suit your needs?
public static void main(String[] args) {
try {
copyJarFromZip("G:\\Dateien\\Desktop\\Desktop.zip",
"G:\\Dateien\\Desktop\\someJar.jar");
} catch (IOException ex) {
ex.printStackTrace();
}
}
public static void copyJarFromZip(final String zipPath, final String targetPath) throws IOException {
try (ZipFile zipFile = new ZipFile(zipPath)) {
for (final Enumeration<? extends ZipEntry> e = zipFile.entries(); e.hasMoreElements();) {
ZipEntry zipEntry = e.nextElement();
if (zipEntry.getName().endsWith(".jar")) {
Files.copy(zipFile.getInputStream(zipEntry), Paths.get(targetPath),
StandardCopyOption.REPLACE_EXISTING);
}
}
}
}