How to password protect a zipped Excel file in Java? - java

I have a question about password protecting an Excel file.
The situation is that, I have a zip file, that has an Excel file in it. I need to write a Java program, to password protect the Excel file. Hence, the user should be able to unzip the file (the zip file need not be password protected). But, the Excel needs to be password-protected. When the user tries to unzip the file, he should be able to do so.
And when he tries to open the Excel file (which is inside the unzipped folder), it must ask for a password. The question is similar to Protect excel file with java, with the added complexity that, the Excel file is zipped.
I have code, that password protects only the zip file, but this is not what I want.
import java.io.File;
import java.util.ArrayList;
import net.lingala.zip4j.core.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.model.ZipParameters;
import net.lingala.zip4j.util.Zip4jConstants;
/**
* Demonstrates adding files to zip file with standard Zip Encryption
*/
public class AddFilesWithStandardZipEncryption
{
public AddFilesWithStandardZipEncryption()
{
try {
// Initiate ZipFile object with the path/name of the zip file.
//ZipFile zipFile = new ZipFile("c:\\ZipTest\\AddFilesWithStandardZipEncryption.zip");
ZipFile zipFile = new ZipFile("C:\\homepage\\workspace\\PasswordProtectedFiles\\new.zip");
// Build the list of files to be added in the array list
// Objects of type File have to be added to the ArrayList
ArrayList filesToAdd = new ArrayList();
//filesToAdd.add(new File("C:\\homepage\\workspace\\passwordprotectedzipfile\\profile\\profile.txt"));
filesToAdd.add(new File("C:\\homepage\\workspace\\PasswordProtectedFiles\\new.xlsx"));
//filesToAdd.add(new File("c:\\ZipTest\\myvideo.avi"));
//filesToAdd.add(new File("c:\\ZipTest\\mysong.mp3"));
// Initiate Zip Parameters which define various properties such
// as compression method, etc.
ZipParameters parameters = new ZipParameters();
parameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE); // set compression method to store compression
// Set the compression level
parameters.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_NORMAL);
// Set the encryption flag to true
// If this is set to false, then the rest of encryption properties are ignored
parameters.setEncryptFiles(true);
// Set the encryption method to Standard Zip Encryption
parameters.setEncryptionMethod(Zip4jConstants.ENC_METHOD_STANDARD);
// Set password
parameters.setPassword("test123!");
// Now add files to the zip file
// Note: To add a single file, the method addFile can be used
// Note: If the zip file already exists and if this zip file is a split file
// then this method throws an exception as Zip Format Specification does not
// allow updating split zip files
zipFile.addFiles(filesToAdd, parameters);
}
catch (ZipException e)
{
e.printStackTrace();
}
}
public static void main(String[] args)
{
new AddFilesWithStandardZipEncryption();
}
}

Without uncompressing, Its impossible to password protect excel which is inside a zip file.
Here is what you can do
Unzip the content using tips in What is the best way to extract a zip file using java and Compressing and Decompressing Data Using Java APIs
Password protect extracted excel file using tips in Password Protected Excel File
Zip the password protected excel file using tips in Java Compress Large File

Use java.util.zip or zip4j to decompress file to some temp direcotry or to memory, if you know it's small.
Then use HSSFWorkbook.writeProtectWorkbook from Apache POI library
Compress Excel workbook again.

I think you should check out truezip (Truezip website). It provides read/write access to ZIP, JAR, EAR, WAR etc and supports appending to existing ZIP files.
I suggest you create your zip file without the excel file in it, create your passworded excel file as directed in the link you provided and then use truezip to write this excel file to the archive. Hope this helps

Related

create zip file without writing to disk

I am working on a Springboot application that has to return a zip file to a frontend when the user downloads some report. I want to create a zip file without writing the zip file or the original files to disk.
The directory I want to zip contains other directories, that contain the actual files. For example, dir1 has subDir1 and subDir2 inside, subDir1 will have two file subDir1File1.pdf and subDir1File2.pdf. subDir2 will also have files inside.
I can do this easily by creating the physical files on the disk. However, I feel it will be more elegant to return these files without writing to disk.
You would use ByteArrayOutputStream if the scope was to write to memory. In essence, the zip file would be entirely contained in memory, so be sure that you don't risk to have too many requests at once and that the file size is reasonable in size! Otherwise this approach can seriously backfire!
You can use following snippet :
public static byte[] zip(final String str) throws IOException {
if (StringUtils.isEmpty(str)) {
throw new IllegalArgumentException("Cannot zip null or empty string");
}
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try (GZIPOutputStream gos = new GZIPOutputStream(bos)) {
gos.write(str.getBytes(StandardCharsets.UTF_8));
}
return bos.toByteArray();
}
But as stated in another answer, make sure you are not risking your program too much by loading everything into your java memory.
Please note that you should stream whenever possible. In your case, you could write your data to https://docs.oracle.com/javase/8/docs/api/index.html?java/util/zip/ZipOutputStream.html.
The only downside of this appproach is: the client won't be able to show a download status bar, because the server will not be able to send the "Content-length" header. That's because the size of a ZIP file can only be known after it has been generated, but the server needs to send the headers first. So - no temporary zip file - no file size beforehand.
You are also talking about subdirectories. This is just a naming issue when dealing with a ZIP stream. Each zip item needs to be named like this: "directory/directory2/file.txt". This will produce subdirectories when unzipping.

Download the files inside compressed .gz files from S3 Bucket

I have a set of .gz compressed files in s3 bucket. I want to download the csv file inside the .gz file. I tried to extract the .gz file and put it into the s3Object. Now i need to extract the s3 object and download the csv file inside it using java. Please advise.This is the code I used.Now i am able to download gz file. But I need to download csv file inside gz.
S3Object object = s3Client.getObject(“bucket”,“Location/file.gz”);
final String encoding = null;
return ResponseEntity.ok(IOUtils.toString(object.getObjectContent(), encoding));
I need help in unzipping the gz file in s3object and return the decompressed contents in the response.
The below code will convert your gunzip file into plain data, but I'm not sure 100% about your actual issue, whether you want to display the content in browser itself or you want to send it as Save as Option, that's why I did a minimum code change to your code assuming you have only problem in converting gunzip format to CSV data, hope you could modify/enhance it that suits you best.
import java.util.zip.GZIPInputStream;
//your method begins from here
final AmazonS3 s3 = AmazonS3ClientBuilder.defaultClient();
S3Object object = s3.getObject("your-bucket", "file-path");
return ResponseEntity.ok(IOUtils.toString(new GZIPInputStream(object.getObjectContent())));

Programatically Extract Single Specific File From 7zip Archive - Java - Linux

I would really appreciate your input on the below scenario please.
The requirements:
- I have a 7zip archive file with several thousands of files in it
- I have a java application running on linux that is required to retrieve individual files from the 7 zip file
I would like to retrieve a file from the archive by its path (e.g. my7zFile.7z/file1.pdf) without having to iterate through all the files in the archive and comparing file names.
I would like to avoid having to extract all files from the archive before running the search (the uncompressed archive is several TB).
I had a look into 7zip Java Binding - specifically the IInArchive class, the only extract method seems to work via file index, not via file name:
http://sevenzipjbind.sourceforge.net/javadoc/net/sf/sevenzipjbinding/IInArchive.html
Do you know of any other libraries that could help me with this use case or am I overlooking a way of doing this with 7zip jbinding?
Thank you
Kind regards,
Tobi
Sadly it appears the API doesn't provide enough to fulfill all your requirements. In order to extract a single file it appears you need to walk the archive index. The simplified interface to the archive makes this much easier:
The ISimpleInArchive interface provides:
ISimpleInArchiveItem[] getArchiveItems()
Allowing you to retrieve an list of items in the archive.
The ISimpleInArchiveItem interface provides the method:
java.lang.String getPath()
Hence you can walk the archiveItems comparing on path. Granted this is against your requirements.
However, note this walks the index table and does not extract the files until requested. Once you have the item your after you can use:
ExtractOperationResult extractSlow(ISequentialOutStream SequentialOutStream)
on the item you have found to actually extract it.
Looking at the 7z file format (note this is not the official site of 7zip), the header information is all at the end of the file with the Signature header at the start of the file giving an offset to the start of the header info. So provided the SevenZip bindings are written nicely, your search will at most read the start of the file (SignatureHeader) to find the offset to the HeaderInfo section, then walk the HeaderInfo section in order to build up the file list required in getArchiveItems(). Only once you have the item you need will it shift back to the index of the actual stream for the file you want extracted (most likely when you call extractSlow).
So whilst not all your requirements are met, the overhead of the search/compare required is limited to only searching the header info of the archive.
Once I wrote a code to read from all the files and folders from a zip file. I had a long file(text)/folder hierarchy inside the zip file. I am not sure whether that will help you or not. I am sharing the skeleton of the code.
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
ZipFile zipFile = new ZipFile(filepath); // filepath of the zip file
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
if (entry.isDirectory()) { // found directory inside the zipFile
// write your code here
} else {
InputStream stream = zipFile.getInputStream(entry);
BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
// write your code to read the content of the file
}
}
You can modify the code to reach your desired file in the zip. But i don't think you will be able to access the file directly rather you have to walk through all the paths of the zip archive. Note that, ZipFile iterates through all file and folders inside a zipped file in DFS (Depth First Search) manner. You will find detailed relevant examples in web.

Read tgz w/out unpacking it onto computer or Unpack as temp & delete when program closes?

Hey guys I'm currently using jarchivelib which can be found Here I'm stuck on figuring out a way to read the file without having to use the unpack method because it makes a file of the unpacked version. EX:
File archive = new File("/home/jack/archive.zip");
File destination = new File("/home/jack/archive");
Archiver archiver = ArchiverFactory.createArchiver(ArchiveFormat.ZIP);
archiver.extract(archive, destination);
I want to make it so i don't have to unpack it to read the files... If there is no way to do that I'm guessing in my method for Jframe.setDefualtCloseOpperation i'll have to make a custom one so it deletes the files? or is there a better way for handling temp files?
If all you want to do is to extract the file, why not use Java's built in zip to extract the file or if it is password protected you can use Zip4j. These libraries support streams, so that you can extract the contents of the file without writing it a FileStream
As of version 0.4.0, the jarchivelib Archiver API supports streaming an archive rather than extracting it directly onto the filesystem.
ArchiveStream stream = archiver.stream(archive);
ArchiveEntry entry;
while((entry = stream.getNextEntry()) != null) {
// access each archive entry individually using the stream
// or extract it using entry.extract(destination)
// or fetch meta-data using entry.getName(), entry.isDirectory(), ...
}
stream.close();
when the stream is pointing to an entry after calling getNextEntry, you can use the stream.read methods just as you would reading an individual entry.

How to extract a single file from a remote archive file?

Given
URL of an archive (e.g. a zip file)
Full name (including path) of a file inside that archive
I'm looking for a way (preferably in Java) to create a local copy of that file, without downloading the entire archive first.
From my (limited) understanding it should be possible, though I have no idea how to do that. I've been using TrueZip, since it seems to support a large variety of archive types, but I have doubts about its ability to work in such a way. Does anyone have any experience with that sort of thing?
EDIT: being able to also do that with tarballs and zipped tarballs is also important for me.
Well, at a minimum, you have to download the portion of the archive up to and including the compressed data of the file you want to extract. That suggests the following solution: open a URLConnection to the archive, get its input stream, wrap it in a ZipInputStream, and repeatedly call getNextEntry() and closeEntry() to iterate through all the entries in the file until you reach the one you want. Then you can read its data using ZipInputStream.read(...).
The Java code would look something like this:
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);
This is, of course, untested.
Contrary to the other answers here, I'd like to point out that ZIP entries are compressed individually, so (in theory) you don't need to download anything more than the directory and the entry itself. The server would need to support the Range HTTP header for this to work.
The standard Java API only supports reading ZIP files from local files and input streams. As far as I know there's no provision for reading from random access remote files.
Since you're using TrueZip, I recommend implementing de.schlichtherle.io.rof.ReadOnlyFile using Apache HTTP Client and creating a de.schlichtherle.util.zip.ZipFile with that.
This won't provide any advantage for compressed TAR archives since the entire archive is compressed together (beyond just using an InputStream and killing it when you have your entry).
Since TrueZIP 7.2, there is a new client API in the module TrueZIP Path. This is an implementation of an NIO.2 FileSystemProvider for JSE 7. Using this API, you can access HTTP URI as follows:
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.
...
}
I'm not sure if there's a way to pull out a single file from a ZIP without downloading the whole thing first. But, if you're the one hosting the ZIP file, you could create a Java servlet which reads the ZIP file and returns the requested file in the response:
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);
}
}

Categories

Resources