How to add duplicate file in Zip using ZipEntry - java

I have a list of files and that list may contains duplicate file name but those files resides in different location with different data. Now when I am trying to add those files in zip I am getting java.lang.Exception: duplicate entry: File1.xlsx. Please suggest how I can add duplicate file names. One solution is like if I can rename the dulpicate file as File , File_1,File_2.. But I am not sure how I can achieve it. Please help !!! Below is my working code if all the file names are unique.
Resource resource = null;
try (ZipOutputStream zippedOut = new ZipOutputStream(response.getOutputStream())) {
for (String file : fileNames) {
resource = new FileSystemResource(file);
if(!resource.exists() && resource != null) {
ZipEntry e = new ZipEntry(resource.getFilename());
//Configure the zip entry, the properties of the file
e.setSize(resource.contentLength());
e.setTime(System.currentTimeMillis());
// etc.
zippedOut.putNextEntry(e);
//And the content of the resource:
StreamUtils.copy(resource.getInputStream(), zippedOut);
zippedOut.closeEntry();
}
}
//zippedOut.close();
zippedOut.finish();
return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=download.zip").body(zippedOut);
} catch (Exception e) {
throw new Exception(e.getMessage());
}

One solution is like if I can rename the duplicate file as File, File_1, File_2, ... But I am not sure how I can achieve it.
Build a Set of names, and append a number to make name unique, if needed, e.g.
Set<String> names = new HashSet<>();
for (String file : fileNames) {
// ...
String name = resource.getFilename();
String originalName = name;
for (int i = 1; ! names.add(name); i++)
name = originalName + "_" + i;
ZipEntry e = new ZipEntry(name);
// ...
}
The code relies on add() returning false if the name is already in the Set, i.e. if name is a duplicate.
This will work even if given names are already numbered, e.g. here is example of mapped names given the order of incoming names:
foo_2
foo
foo -> foo_1
foo -> foo_3 foo_2 was skipped
foo -> foo_4
foo_1 -> foo_1_1 number appended to make unique

this solution copies the index style used by windows, "filename (n).doc", respecting the file extensions
Map<String, Integer> nombresRepeticiones = new HashMap<String, Integer>();
for(FileDTO file : files) {
String filename = file.getNombre();
if (nombresRepeticiones.containsKey(filename)) {
int numeroRepeticiones = nombresRepeticiones.get(filename) + 1;
String base = FilenameUtils.removeExtension(filename);
String extension = FilenameUtils.getExtension(filename);
filename = base + " (" + numeroRepeticiones + ")";
if (extension != null && !extension.isEmpty()) {
filename = filename + "." + extension;
}
nombresRepeticiones.put(file.getNombre(), numeroRepeticiones);
}
else {
nombresRepeticiones.put(filename, 0);
}
ZipEntry ze = new ZipEntry(filename);
zos.putNextEntry(ze);
zos.write(IOUtils.toByteArray(file.getContenido().getInputStream()));
zos.closeEntry();
}

Related

Pick a particular file from directory using java

I need some help in picking the exact file from directory on Windows using Java. Below is the code which I have written for this. But when I pass my input as "XYZ" file it always picks the "ABC" file only.
My input to pick the file was 'XYZ', but output always returns 'ABC' file
directoryPath = new File(System.getProperty("user.dir") + "\\TestFilesDirectory\\");
String contents[] = directoryPath.list();
System.out.println("List of files and directories in the specified directory : " + directoryPath);
for (int i = 0; i < contents.length; i++) {
// System.out.println(contents[i]);
ArrayList<String> fileNameList = new ArrayList<String>();
String[] fileNameSplit = contents[i].split("_");
for (int k = 0; k < fileNameSplit.length; k++) {
// System.out.println(fileNameSplit[k].toUpperCase());
fileNameList.add(fileNameSplit[k].toUpperCase());
}
if (fileNameList.contains("ABC") {
System.out.println("Pick ABC file from directory ");
source = new File(directoryPath + "\\" + contents[i] + "");
}
System.out.println("Base File: " + source);
else if (fileNameList.contains("DEF") {
System.out.println("Pick DEF file from directory ");
source = new File(directoryPath + "\\" + contents[i] + "");
}
else if (fileNameList.contains("XYZ") {
System.out.println("Pick XYZ file from directory ");
source = new File(directoryPath + "\\" + contents[i] + "");
}
Below are my files in Test directory:
I think that your code can be improved. As far as I can tell you need to retrieve the path of a given file matching either the first or second part of its filename. So for example:
DEF_GHI.txt
can be matched by either inputting def or ghi into your search method. The easiest and most concise way to do so would be:
public Optional<File> findByName(String name, String directoryPath) {
try (var fileStream = Files.list(Path.of(directoryPath))) {
return fileStream
.filter(path -> Arrays.stream(path.getFileName().toString().split("_"))
.map(part -> part.replaceAll("\\.[^.]*$", ""))
.anyMatch(part -> part.equalsIgnoreCase(name)))
.findAny()
.map(Path::toFile);
} catch (IOException e) {
System.err.println("Unable to open directory stream for path:: " + directoryPath);
e.printStackTrace();
}
return Optional.empty();
}
What this does is the following:
Accepts a filename and a path representing a directory to search at
Creates a stream of paths representing the files under the given directory
Gets the filename for each given path and performs the following:
Splits the string using _ in order to retrieve both parts (if present)
Replaces everything after the . character (including it) in order to get rid of the file extension.
Attempts to match each part against the provided name parameter
Map the found path value to a File (if present)
Lastly returns either the optionally wrapped File or an empty Optional.
I hope this is what you are looking for.

Get File from FileItem and save that file to path

I need to save an Image to my Desktop, but i cannot get that Image file, when I save it, I just save nothing, an empty file, I don't know how to get that file from FileItem.
for (FileItem lFileItem: c.emptyIfNull(pImageLogo))
{
long lFileSize = lFileItem.getSize();
String lFileName = lFileItem.getName();
String lExtensionFile = FilenameUtils.getExtension(lFileName);
String lContentType = lFileItem.getContentType();
if (lContentType.startsWith("image/")) {
if (lFileSize < 100 || lFileSize > Integer.valueOf(lLimitFileSize))
{
lShowAlert=c.msg("userReg.alertSizeFileErrorPart1","File size must be smaller than ") + Integer.valueOf(lLimitFileSize)/1000000 + c.msg("userReg.alertSizeFileErrorPart2","MB and greater than 1KB ");
throw new ErrorControl(lShowAlert);
break;
}
if (lFileSize<=0) break;
c.log(this, "file size="+lFileSize+" max allowed="+lLimitFileSize);
File lFile = new File(logoClientsFolder+"logo_"+ lIdClient + "." + lExtensionFile);
if(lFile.createNewFile())
{
System.out.println("File created: " + lFile.getName());
}
} else {
System.out.println("IS NOT AN IMAGE");
}
}
Can you help me please? Thanks!
you only create the File but don't write to it
you need something like:
final Path path = Paths.get(filename);
OutputStream outStream = Files.newOutputStream(path, StandardOpenOption.CREATE_NEW);
(add exception handling)
and then write the content to that outStream
and you should use NIO for file access, see:
Java: Path vs File

Unable to extract zip file through Java code made in nodeJs code

What I want to do, is to create a zip file in nodeJs and later extract it through Java code(An already written Java program to work on the zip).
I am able to create zip file in nodeJs using jszip but my java code is unable to extract that zip file(Though my finder can extract it, even my extract code in nodeJs also works).
nodeJs code to create zip:
this.zip = function (sourceDirectory, zipFile, excludParent) {
var zip = new JSZip();
var list = getFiles(sourceDirectory, excludParent);
for (var i = 0; i < list.length; i++) {
var obj = list[i];
if(typeof(obj.source) == "undefined"){
if (excludParent)
zip.folder(obj.target.substring(obj.target.indexOf("/") + 1));
else
zip.folder(obj.target);
}else {
if (excludParent)
zip.file(obj.target.substring(obj.target.indexOf("/") + 1), fs.readFileSync(obj.target), {base64: true});
else
zip.file(obj.target, fs.readFileSync(obj.target), {base64: true});
}
}
zip.generateNodeStream({type:'nodebuffer', streamFiles:true, compressionOptions:'DEFAULT'})
.pipe(fs.createWriteStream(zipFile))
.on('finish', function () {
console.log(zipFile + " written.");
})
};
var getFiles = function (sourceDirectory, excludeParent) {
var list = [];
if (excludeParent) {
if (fs.lstatSync(sourceDirectory).isDirectory()) {
var fileList = fs.readdirSync(sourceDirectory);
for (var i = 0; i < fileList.length; i++) {
list = list.concat(getFiles_(sourceDirectory + sep, fileList[i]));
}
}
} else {
list = getFiles_("", sourceDirectory);
}
return list;
};
var getFiles_ = function (parentDir, path) {
var list = [];
if (path.indexOf(".") == 0) return list;
if (fs.lstatSync(parentDir + path).isDirectory()) {
list.push({target: parentDir + path});
var fileList = fs.readdirSync(parentDir + path);
for (var i = 0; i < fileList.length; i++) {
list = list.concat(getFiles_(parentDir + path + sep, fileList[i]));
}
} else {
list.push({source: parentDir + path, target: parentDir + path});
}
return list;
};
Java code to extract file:
public void unzip(String zipFilePath, String destDirectory) throws IOException {
File destDir = new File(destDirectory);
if (!destDir.exists()) {
destDir.mkdir();
}
ZipInputStream zipIn = new ZipInputStream(new FileInputStream(zipFilePath));
ZipEntry entry = zipIn.getNextEntry();
// iterates over entries in the zip file
while (entry != null) {
String filePath = destDirectory + File.separator + entry.getName();
if (!entry.isDirectory()) {
// if the entry is a file, extracts it
extractFile(zipIn, filePath);
} else {
// if the entry is a directory, make the directory
File dir = new File(filePath);
dir.mkdirs();
}
zipIn.closeEntry();
entry = zipIn.getNextEntry();
}
zipIn.close();
}
This code throws following stacktrace:
java.util.zip.ZipException: only DEFLATED entries can have EXT descriptor
at java.util.zip.ZipInputStream.readLOC(ZipInputStream.java:310)
at java.util.zip.ZipInputStream.getNextEntry(ZipInputStream.java:122)
at module.builder.ZipUtility.unzip(ZipUtility.java:57)
at module.builder.DataPatcher.unZipAndroidImageFiles(DataPatcher.java:624)
at module.builder.DataPatcher.patchAndroid(DataPatcher.java:145)
at module.builder.FXMLDocumentController$5.call(FXMLDocumentController.java:324)
at module.builder.FXMLDocumentController$5.call(FXMLDocumentController.java:312)
at javafx.concurrent.Task$TaskCallable.call(Task.java:1423)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.lang.Thread.run(Thread.java:745)
This java code works fine if i create a zip file through an application (i.e winzip, winrar, zip made by finder etc.) but it throws ava.util.zip.ZipException: only DEFLATED entries can have EXT descriptor while extracting zip made through above mentioned nodeJs method.
I ran into the same problem today.
The issue is with the streamFiles:true passed as an option to zip.generateNodeStream(). From the docs:
When this options is true, we stream the file and use data descriptors at > the end of the entry. This option uses less memory but some program might > not support data descriptors (and won’t accept the generated zip file).
Removing the streamFiles:true option should work as long as you are not worried about memory usage.

Rename file in JFileChooser if file exists and user inputs extension

The code works as it has to until user inputs a filename with extension (.txt) and it already exists. So if the file "test.txt" exists and the user decides to name the new file as "test", it will be named as "test(1).txt", but if the user adds extension like "test.txt", the file will be named as "test.txt" and the next file user names "test.txt" will be saved as "test.txt(1).txt".
Is it possible to get the name of file from JFileChooser, remove it's extension if user input it and use it as name of the new file after adding number in the middle of original file name and it's extension? I can get name without extension as String type, but I need it as File type.
File ft = fc.getSelectedFile();
String ext = ".txt";
File tmp = new File(ft.getPath());
if (!fc.getSelectedFile().getAbsolutePath().endsWith(ext)){
ft = new File (ft + ext);
}
File test = new File(ft.getPath());
File temp = new File(ft.getPath());
File temp1 = new File(ft.getPath());
int count = 1;
while (temp.exists()) {
if(tmp.getAbsolutePath().endsWith(ext)){
}
File ft1 = new File (tmp + "(" + count + ")");
ft = new File (tmp + "(" + count + ")" + ext);
count++;
temp = new File(ft.getPath());
temp1 = new File(ft1.getPath());
}
if (!temp1.getAbsolutePath().endsWith(ext)){
ft = new File (temp1 + ext);
}
int cnt = count - 1;
if (!test.equals(temp)){
JOptionPane.showMessageDialog(null, "File already exists. So it's saved with (" + cnt + ") at the end.");
}
OK so I've tried to make this work without changing your code too much. Try this:
String filePath = fc.getSelectedFile().getAbsolutePath();
final String ext = ".txt";
String filePathWithoutExt;
if (filePath.endsWith(ext)) {
filePathWithoutExt = filePath.substring(0, filePath.length() - ext.length());
} else {
filePathWithoutExt = filePath;
}
File test = new File(filePathWithoutExt + ext);
File temp = new File(filePathWithoutExt + ext);
int count = 0;
while (temp.exists()) {
count++;
temp = new File(filePathWithoutExt + "(" + count + ")" + ext);
}
if (!test.equals(temp)) {
JOptionPane.showMessageDialog(null,
"File already exists. So it's saved with (" + count + ") at the end.");
}
EDIT:
By the recommendation of Marco N. it could be better to determine whether or not an extension exists by finding the last position of the . since this would also work with extensions other than ".txt". This value would then be used to split the string. The replacement code would look like this:
final int lastPeriodPos = filePath.lastIndexOf(".");
if (lastPeriodPos >= 0) {
filePathWithoutExt = filePath.substring(0, lastPeriodPos);
} else {
filePathWithoutExt = filePath;
However this would also have some issues if the user entered a file name that contained the . anywhere other than just before the file extension.
Hmm, I think this entry might be useful as well:
Remove filename extension in Java
I currently lack the time to properly test it (or better test it at all) but shouldn't it work this way:
public static String removeExtention(File f) {
String name = f.getName();
// Now we know it's a file - don't need to do any special hidden
// checking or contains() checking because of:
final int lastPeriodPos = name.lastIndexOf('.');
if (lastPeriodPos <= 0)
{
// No period after first character - return name as it was passed in
return f;
}
else
{
// Remove the last period and everything after it
File renamed = new File(f.getParent(), name.substring(0, lastPeriodPos));
return renamed;
}
}
I briefly tried to adjust the code from the posting mentioned above and it may very well contain some errors or flaws. (If you find some, do not hesitate to comment on them. Some of them might be due to my current lack of time, but I am always willing to learn and improve.) However I hope this may help you to find a proper solution to your problem.

Zip4j missing the last file added

I'm trying to zip a bunch of files using the Zip4j library. I pass a list of the file paths of the files I want to compress and I add them one by one into the zip file. For some reason, the last file does not get added. I checked the indexes of the loop and I'm pretty sure they're correct. I am not getting any exceptions or error messages. Here's my code:
// get the path; paths refers to the list of files to compress
String uuidString = UUID.randomUUID().toString();
String path = "H:/public/ZipFiles/" + uuidString + ".zip";
try {
// create the new zip file
ZipFile zipFile = new ZipFile(path);
File fileToAdd;
String message = "";
ZipParameters parameters = new ZipParameters();
// set compression method to store compression
parameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE);
// Set the compression level
parameters.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_NORMAL);
// add each file to the zipFile
for(int i = 0; i < paths.size(); i++)
{
fileToAdd = new File(paths.get(i));
if(fileToAdd.exists())
{
System.out.println("writing file at " + paths.get(i) + " to the zip file");
zipFile.addFile(fileToAdd, parameters);
}
else
{
message += "File with at path " + paths.get(i) + " was not found.\n";
}
}
} catch (ZipException e) {
e.printStackTrace();
}
All the file paths get printed when they are added. Any ideas?
You're not closing the ZipFile.
I think there is a problem with the jar file from their own website at http://www.lingala.net/zip4j/download.php
But when I downloaded it from the maven repository at https://mvnrepository.com/artifact/net.lingala.zip4j/zip4j/1.3.2 , it is working perfectly.

Categories

Resources