通过独立的块压缩文件,然后将它们连接成一个有效的存档
compressing file by independent chunks and then concat them into one valid archive
我想知道是否可以通过独立的块压缩任意文件(或文件夹,或任何其他文件结构),然后通过将它们连接在一起来获得有效的存档(例如 gzip)。一些要求:
- java 8
- 块 <= 16MB
- 文件夹结构在此过程中没有改变
- 块被独立压缩,但顺序被保留
- 每个压缩块都附加到生成的存档的末尾
- 生成的存档应该是有效的并且可以通过任何标准工具解压缩
看起来我需要先创建一个存档 header,然后再将压缩块附加到它 https://www.rfc-editor.org/rfc/rfc1952,但是我不确定它是否被任何人支持标准 java 实用程序或第 3 方库。有人知道从哪里开始吗?
一些背景:
我有一个 client-server 应用程序,它允许用户将文件上传到云存储。通过 REST api 通信,客户端将负责将文件分成块并一个一个地上传。可以在浏览器中进行压缩,但是我想知道我们是否可以将该负载移动到后端。
您可以为 tar + gzip 尝试这样的操作:
Maven 依赖项:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>1.18</version>
</dependency>
Java 压缩成块的代码:
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;
import org.apache.commons.compress.utils.IOUtils;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
[..]
private static final int MAX_CHUNK_SIZE = 16000000;
public void compressTarGzChunks(String inputDirPath, String outputDirPath) throws Exception {
PipedInputStream in = new PipedInputStream();
final PipedOutputStream out = new PipedOutputStream(in);
new Thread(() -> {
try {
int chunkIndex = 0;
int n = 0;
byte[] buffer = new byte[8192];
do {
String chunkFileName = String.format("archive-part%d.tar.gz", chunkIndex);
try (OutputStream fOut = Files.newOutputStream(Paths.get(outputDirPath, chunkFileName));
BufferedOutputStream bOut = new BufferedOutputStream(fOut);
GzipCompressorOutputStream gzOut = new GzipCompressorOutputStream(bOut)) {
int currentChunkSize = 0;
if (chunkIndex > 0) {
gzOut.write(buffer, 0, n);
currentChunkSize += n;
}
while ((n = in.read(buffer)) != -1 && currentChunkSize + n < MAX_CHUNK_SIZE) {
gzOut.write(buffer, 0, n);
currentChunkSize += n;
}
chunkIndex++;
}
} while (n != -1);
in.close();
} catch (IOException e) {
// logging and exception handling should go here
}
}).start();
try (TarArchiveOutputStream tOut = new TarArchiveOutputStream(out)) {
compressTar(tOut, inputDirPath, "");
}
}
private static void compressTar(TarArchiveOutputStream tOut, String path, String base)
throws IOException {
File file = new File(path);
String entryName = base + file.getName();
TarArchiveEntry tarEntry = new TarArchiveEntry(file, entryName);
tarEntry.setSize(file.length());
tOut.putArchiveEntry(tarEntry);
if (file.isFile()) {
try (FileInputStream in = new FileInputStream(file)) {
IOUtils.copy(in, tOut);
tOut.closeArchiveEntry();
}
} else {
tOut.closeArchiveEntry();
File[] children = file.listFiles();
if (children != null) {
for (File child : children) {
compressTar(tOut, child.getAbsolutePath(), entryName + "/");
}
}
}
}
Java 将块连接成单个存档的代码:
public void concatTarGzChunks(List<InputStream> sortedTarGzChunks, String outputFile) throws IOException {
try {
try (FileOutputStream fos = new FileOutputStream(outputFile)) {
for (InputStream in : sortedTarGzChunks) {
int len;
byte[] buf = new byte[1024 * 1024];
while ((len = in.read(buf)) != -1) {
fos.write(buf, 0, len);
}
}
}
} finally {
sortedTarGzChunks.forEach(is -> {
try {
is.close();
} catch (IOException e) {
// logging and exception handling should go here
}
});
}
}
是的。根据标准 (RFC 1952),gzip 文件的串联是一个有效的 gzip 文件。 gzip 当然可以处理这个问题。
您担心某些代码可能不支持它是正确的,因为连接 gzip 成员并不常见。如果你想超级安全,你可以将 gzip 文件组合成一个 gzip 成员,而不必重新压缩。但是,您确实需要通读所有压缩数据,有效地将其解压缩到内存中(这仍然比压缩快得多)。您可以在 gzjoin.c.
中找到相关示例
我想知道是否可以通过独立的块压缩任意文件(或文件夹,或任何其他文件结构),然后通过将它们连接在一起来获得有效的存档(例如 gzip)。一些要求:
- java 8
- 块 <= 16MB
- 文件夹结构在此过程中没有改变
- 块被独立压缩,但顺序被保留
- 每个压缩块都附加到生成的存档的末尾
- 生成的存档应该是有效的并且可以通过任何标准工具解压缩
看起来我需要先创建一个存档 header,然后再将压缩块附加到它 https://www.rfc-editor.org/rfc/rfc1952,但是我不确定它是否被任何人支持标准 java 实用程序或第 3 方库。有人知道从哪里开始吗?
一些背景: 我有一个 client-server 应用程序,它允许用户将文件上传到云存储。通过 REST api 通信,客户端将负责将文件分成块并一个一个地上传。可以在浏览器中进行压缩,但是我想知道我们是否可以将该负载移动到后端。
您可以为 tar + gzip 尝试这样的操作:
Maven 依赖项:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>1.18</version>
</dependency>
Java 压缩成块的代码:
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;
import org.apache.commons.compress.utils.IOUtils;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
[..]
private static final int MAX_CHUNK_SIZE = 16000000;
public void compressTarGzChunks(String inputDirPath, String outputDirPath) throws Exception {
PipedInputStream in = new PipedInputStream();
final PipedOutputStream out = new PipedOutputStream(in);
new Thread(() -> {
try {
int chunkIndex = 0;
int n = 0;
byte[] buffer = new byte[8192];
do {
String chunkFileName = String.format("archive-part%d.tar.gz", chunkIndex);
try (OutputStream fOut = Files.newOutputStream(Paths.get(outputDirPath, chunkFileName));
BufferedOutputStream bOut = new BufferedOutputStream(fOut);
GzipCompressorOutputStream gzOut = new GzipCompressorOutputStream(bOut)) {
int currentChunkSize = 0;
if (chunkIndex > 0) {
gzOut.write(buffer, 0, n);
currentChunkSize += n;
}
while ((n = in.read(buffer)) != -1 && currentChunkSize + n < MAX_CHUNK_SIZE) {
gzOut.write(buffer, 0, n);
currentChunkSize += n;
}
chunkIndex++;
}
} while (n != -1);
in.close();
} catch (IOException e) {
// logging and exception handling should go here
}
}).start();
try (TarArchiveOutputStream tOut = new TarArchiveOutputStream(out)) {
compressTar(tOut, inputDirPath, "");
}
}
private static void compressTar(TarArchiveOutputStream tOut, String path, String base)
throws IOException {
File file = new File(path);
String entryName = base + file.getName();
TarArchiveEntry tarEntry = new TarArchiveEntry(file, entryName);
tarEntry.setSize(file.length());
tOut.putArchiveEntry(tarEntry);
if (file.isFile()) {
try (FileInputStream in = new FileInputStream(file)) {
IOUtils.copy(in, tOut);
tOut.closeArchiveEntry();
}
} else {
tOut.closeArchiveEntry();
File[] children = file.listFiles();
if (children != null) {
for (File child : children) {
compressTar(tOut, child.getAbsolutePath(), entryName + "/");
}
}
}
}
Java 将块连接成单个存档的代码:
public void concatTarGzChunks(List<InputStream> sortedTarGzChunks, String outputFile) throws IOException {
try {
try (FileOutputStream fos = new FileOutputStream(outputFile)) {
for (InputStream in : sortedTarGzChunks) {
int len;
byte[] buf = new byte[1024 * 1024];
while ((len = in.read(buf)) != -1) {
fos.write(buf, 0, len);
}
}
}
} finally {
sortedTarGzChunks.forEach(is -> {
try {
is.close();
} catch (IOException e) {
// logging and exception handling should go here
}
});
}
}
是的。根据标准 (RFC 1952),gzip 文件的串联是一个有效的 gzip 文件。 gzip 当然可以处理这个问题。
您担心某些代码可能不支持它是正确的,因为连接 gzip 成员并不常见。如果你想超级安全,你可以将 gzip 文件组合成一个 gzip 成员,而不必重新压缩。但是,您确实需要通读所有压缩数据,有效地将其解压缩到内存中(这仍然比压缩快得多)。您可以在 gzjoin.c.
中找到相关示例