Java - Create a ZIP file with duplicate entries
Asked Answered
A

4

6

I'd like to create a zip which stores two different files with the same name, but I'm unable (using java.util.zip.ZipOutputStream) due to

java.util.zip.ZipException: duplicate entry:

exception. I know that it's possible, but I need an advise which library I can use for that purposes. Thanks!

UPD the code I'm using:

File zipFile = new File("C:\\Users\\user\\Desktop\\old.zip");
File outFile = new File("C:\\Users\\user\\Desktop\\new.zip");
if(!outFile.exists()) {
    outFile.getParentFile().mkdirs();
    outFile.createNewFile();
}

byte[] buf = new byte[1024];
ZipInputStream zin = new ZipInputStream(new FileInputStream(zipFile));
ZipOutputStream out = new ZipOutputStream(new FileOutputStream(outFile));

ZipEntry entry = zin.getNextEntry();
while (entry != null) {
    String name = entry.getName();
    out.putNextEntry(new ZipEntry(name));
    int len;
    while ((len = zin.read(buf)) > 0) {
        out.write(buf, 0, len);
    }
    entry = zin.getNextEntry();

    if("file".equals(name)) {
        File fakeFile = new File("C:\\Users\\user\\Desktop\\file");
        InputStream in = new FileInputStream(fakeFile);
        out.putNextEntry(new ZipEntry("file"));
        while ((len = in.read(buf)) > 0) {
            out.write(buf, 0, len);
        }
        out.closeEntry();
        in.close();
    }
} 
zin.close();
out.close();
Assonance answered 10/10, 2016 at 12:37 Comment(6)
Please include a MCVE in your postSolemnize
I do not think its possible with current Oracle JVM implementation. Why you need to do this?Chalybeate
@Vampire, addedAssonance
You know that it's possible but you've just proven that it isn't possible. Make up your mind.Longfellow
@JIV, to bypass a signature restriction (signature checker verifies the first entity, but installed installs the last)Assonance
@EJP, a library does not allow duplicates. You may check sources hg.openjdk.java.net/jdk7/jdk7/jdk/file/9b8c96f96a0f/src/share/… line 214: if (! names.add(e.name)) { but you may check where names object is used and suddenly only to disallow duplicates. So it's possible but not with that libraryAssonance
A
10

I was able to bypass restriction thru reflection api:

Field namesField = ZipOutputStream.class.getDeclaredField("names");
namesField.setAccessible(true);
HashSet<String> names = (HashSet<String>) namesField.get(out);

And clearing names after each putNextEntry call

Assonance answered 10/10, 2016 at 13:17 Comment(4)
You must have really wanted to get those duplicate entries saved to figure out this technique! (p.s. thanks! very handy!)Snowmobile
Yes, this is very handy if you want to prepare a JAR file for a test with e.g. multiple entries for ServiceLoader.Pragmatics
@Julius Musseau Yepp, pkzip once used to maintain a folder structure and store file names, only instead of tons of redundant info.Tmesis
Now that Java 17 bans the use of reflection into java.* packages we need a new way !Snowmobile
T
0

Putting Ivans answer in something performant.

static final Field zipNamesField = getZipNameField();

private static Field getZipNameField()
{
    try
    {
        Field res = ZipOutputStream.class.getDeclaredField("names");
        res.setAccessible(true);
        return res;
    }
    catch (NoSuchFieldException | SecurityException e)
    {
        e.printStackTrace();
        return null;
    }
}

...

// Loop
((HashSet<String>) zipNamesField.get(out)).clear();
entry = new ZipEntry(...);
out.putNextEntry(entry);

...
Tmesis answered 11/7, 2023 at 9:25 Comment(0)
T
-1
try (FileOutputStream fos = new FileOutputStream(zipFile);
            ZipOutputStream zos = new ZipOutputStream(fos)) {

        HashSet<String> names = new HashSet<String>();
        for (String filePath : fileList) {
            if(names.add(filePath))
            {
            String name = filePath.substring(directory.getAbsolutePath()
                    .length() + 1, filePath.length());
            ZipEntry zipEntry = new ZipEntry(name);

            zos.putNextEntry(zipEntry);
            try (FileInputStream fis = new FileInputStream(filePath)) {
                byte[] buffer = new byte[1024];
                int length;
                while ((length = fis.read(buffer)) >= 0) {
                    zos.write(buffer, 0, length);
                }

            } catch (Exception e) {
                e.printStackTrace();
                throw new Exception();
            }
            zos.closeEntry();
            }

        }
        names.clear();
    } catch (IOException e) {
        e.printStackTrace();
        throw new Exception();
    }

By using HashSet solved my issue of zip duplicate entry

Tobin answered 9/1, 2020 at 9:50 Comment(0)
R
-1

This is my solution: adding postfixes for duplicate names:

String SPLIT_FILENAME_EXTENSION_REGEX = "\\.(?=[^\\.]+$)";
Map<String, Integer> namesPostfixes = new HashMap<>();


private void addZipPart(String fullName, InputStream attachmentStream, ZipOutputStream zos,
        Map<String, Integer> namesPostfixes)
        throws IOException, MessagingException {
    String generatedName;
    if (namesPostfixes.containsKey(fullName)) {
        String namePart;
        String extensionPart;
        if (fullName.contains(".")) {
            String[] splittedName = fullName.split(SPLIT_FILENAME_EXTENSION_REGEX);
            namePart = splittedName[0];
            extensionPart = "." + splittedName[1];
        } else {
            namePart = fullName;
            extensionPart = "";
        }
        Integer counter = namesPostfixes.get(fullName);
        generatedName = namePart + "_" + ++counter + extensionPart;
        namesPostfixes.put(fullName, counter);
    } else {
        generatedName = fullName;
        namesPostfixes.put(fullName, 0);
    }
    zos.putNextEntry(
            new ZipEntry(generatedName));
    IOUtils.copy(attachmentStream, zos);
    zos.closeEntry();
}
Ratel answered 19/11, 2021 at 11:14 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Bowen

© 2022 - 2024 — McMap. All rights reserved.