Reputation: 6558
I would like to compress an String manually defined to 7z in Java. So then I could convert it to base64. I found many examples compressing files to 7z and then saving into new File.
I just try the next code, and it takes correctly the file and compress it:
private static void addToArchiveCompression(SevenZOutputFile out, File file, String dir) throws IOException {
String name = dir + File.separator + file.getName();
if (file.isFile()){
SevenZArchiveEntry entry = out.createArchiveEntry(file, name);
out.putArchiveEntry(entry);
FileInputStream in = new FileInputStream(file);
byte[] b = new byte[1024];
int count = 0;
while ((count = in.read(b)) > 0) {
out.write(b, 0, count);
}
out.closeArchiveEntry();
} else if (file.isDirectory()) {
File[] children = file.listFiles();
if (children != null){
for (File child : children){
addToArchiveCompression(out, child, name);
}
}
} else {
System.out.println(file.getName() + " is not supported");
}
}
But how can I compress an manually defined String to 7z and convert it to byte[]? So then I can convert the byte[] to base64 and print it, without generating or reading new Files?
Upvotes: 5
Views: 1747
Reputation: 636
I found that someone had implemented the code.
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry;
import org.apache.commons.compress.archivers.sevenz.SevenZFile;
import org.apache.commons.compress.archivers.sevenz.SevenZOutputFile;
import org.apache.commons.compress.utils.SeekableInMemoryByteChannel;
/**
* the SevenZ Util
*
* @author ph3636
*/
public class SevenZUtil {
private static final int BUFFER_SIZE = 8192;
public static byte[] compress(byte[] bytes) {
if (bytes == null) {
throw new NullPointerException("bytes is null");
}
SeekableInMemoryByteChannel channel = new SeekableInMemoryByteChannel();
try (SevenZOutputFile z7z = new SevenZOutputFile(channel)) {
SevenZArchiveEntry entry = new SevenZArchiveEntry();
entry.setName("sevenZip");
entry.setSize(bytes.length);
z7z.putArchiveEntry(entry);
z7z.write(bytes);
z7z.closeArchiveEntry();
z7z.finish();
return channel.array();
} catch (IOException e) {
throw new RuntimeException("SevenZ compress error", e);
}
}
public static byte[] decompress(byte[] bytes) {
if (bytes == null) {
throw new NullPointerException("bytes is null");
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
SeekableInMemoryByteChannel channel = new SeekableInMemoryByteChannel(bytes);
try (SevenZFile sevenZFile = new SevenZFile(channel)) {
byte[] buffer = new byte[BUFFER_SIZE];
while (sevenZFile.getNextEntry() != null) {
int n;
while ((n = sevenZFile.read(buffer)) > -1) {
out.write(buffer, 0, n);
}
}
return out.toByteArray();
} catch (IOException e) {
throw new RuntimeException("SevenZ decompress error", e);
}
}
}
<commons-compress.version>1.22</commons-compress.version>
<xz.version>1.9</xz.version>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>${commons-compress.version}</version>
</dependency>
<dependency>
<groupId>org.tukaani</groupId>
<artifactId>xz</artifactId>
<version>${xz.version}</version>
</dependency>
reference: SevenZUtil.java
Upvotes: 0
Reputation: 9206
This is a bit offtopic, but assuming that LZMA is part of 7zip, this might help you:
public static byte[]compress(byte[]arr,int level){
try {
ByteArrayOutputStream compr = new ByteArrayOutputStream();
LZMA2Options options = new LZMA2Options();
options.setPreset(level); // play with this number: 6 is default but 7 works better for mid sized archives ( > 8mb)
XZOutputStream out = new XZOutputStream(compr, options);
out.write(arr);
out.finish();
return compr.toByteArray();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static byte[]decompress(byte[]bts){
try {
ByteArrayInputStream bis = new ByteArrayInputStream(bts);
XZInputStream is = new XZInputStream(bis);
ByteArrayInputStream decomp = new ByteArrayInputStream(is.readAllBytes());
ObjectInputStream ois = new ObjectInputStream(decomp);
byte data[]= (byte[]) ois.readObject();
return data;
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
maven dependency:
<dependency>
<groupId>org.tukaani</groupId>
<artifactId>xz</artifactId>
<version>1.8</version>
</dependency>
convert your String to byte[] with s.getBytes(StandardCharsets.UTF_8);
Upvotes: 1
Reputation: 44932
Since you are already using commons-compress for 7zip compression you can create SevenZOutputFile(SeekableByteChannel)
instance with SeekableInMemoryByteChannel
that wraps a byte array. As per javadoc:
A SeekableByteChannel implementation that wraps a byte[].
When this channel is used for writing an internal buffer grows to accommodate incoming data. A natural size limit is the value of Integer.MAX_VALUE. Internal buffer can be accessed via array().
Something like:
SeekableInMemoryByteChannel channel = new SeekableInMemoryByteChannel(new byte[1024]);
SevenZOutputFile out = new SevenZOutputFile(channel);
// modified addToArchiveCompression(out, ...); for String
// encode channel.array() to Base64
Upvotes: 4
Reputation: 1410
You will of course have to apply several changes to the code you posted. That code is meant to compress a file or directory whereas your case is much simpler. You definitely won't need a for
loop, for example.
I break down the various parts you'll have to look into and leave the coding to you.
Converting a String to data for 7z:
One of the options is to use a ByteArrayInputStream
instead of a FileInputStream
. The ByteArrayInputStream
will have to be initialised with the bytes corresponding to the String.
See the following article for an example on how to do this conversion:
https://www.baeldung.com/convert-string-to-input-stream
Converting output bytes to Base64:
There are several approaches, detailed in this StackOverflow thread:
How do I convert a byte array to Base64 in Java?
Outputting 7z to memory instead of File:
You will have to use the SevenZOutputFile
constructor taking a SeekableByteChannel
interface as input. Your implementation of SeekableByteChannel
will have to be backed by a byte array or stream of sorts. You can use the following implementation:
Obtaining a SevenZArchiveEntry
from something other than a file:
Although the SevenZOutputFile
class doesn't seem to provide a facility to do that, if you look at its sourcecode you'll notice that you can manually create a SevenZArchiveEntry
without any intermediation, as it has an empty constructor. You'll have to "pretend" it's still an actual file, but that shouldn't be a problem.
Sourcecode of SevenZArchiveEntry
:
Upvotes: 0