perf(传输): 已压缩格式组包时不再二次压缩
pptx/docx/xlsx/zip/mp4/jpg 这类本身已压缩的格式,再跑一遍 level 6 deflate 压缩率接近 0,但 CPU 照样全量消耗(约 0.04 秒/MB)。改为按文件名后缀判断, 命中清单的 entry 用 Deflater.NO_COMPRESSION,其余保持默认 level 6。 不用 ZipEntry.STORED 是因为它要求预先提供 size 和 CRC-32,会多一遍顺序读; level 0 产出的仍是合法 zip(DEFLATE 存储块),接收端解包与校验逻辑无需改动。 清单从严,只收确定已压缩的格式,避免可压缩文件被误判导致包体变大。 元数据 JSON 可压缩,写它之前显式还原 level。
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
package com.senmesh.sfile.service.impl;
|
package com.senmesh.sfile.service.impl;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.senmesh.common.exception.util.Assert;
|
import com.senmesh.common.exception.util.Assert;
|
||||||
import com.senmesh.common.util.JsonUtils;
|
import com.senmesh.common.util.JsonUtils;
|
||||||
import com.senmesh.common.util.UserContextHolder;
|
import com.senmesh.common.util.UserContextHolder;
|
||||||
@@ -32,6 +33,9 @@ import java.nio.file.Files;
|
|||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.zip.Deflater;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipOutputStream;
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
@@ -45,6 +49,14 @@ public class TransferUserFileServiceImpl implements TransferUserFileService {
|
|||||||
/** 传输目录多为 bind mount,单次 syscall 成本可达毫秒级,小缓冲会把耗时放大数十倍 */
|
/** 传输目录多为 bind mount,单次 syscall 成本可达毫秒级,小缓冲会把耗时放大数十倍 */
|
||||||
private static final int TRANSFER_BUFFER_SIZE = 64 * 1024;
|
private static final int TRANSFER_BUFFER_SIZE = 64 * 1024;
|
||||||
|
|
||||||
|
/** 本身已压缩的格式再跑一遍 deflate 压缩率接近 0,但 CPU 照样全量消耗,故存原样。清单从严,只收确定已压缩的 */
|
||||||
|
private static final Set<String> PRE_COMPRESSED_SUFFIXES = ImmutableSet.of(
|
||||||
|
"zip", "rar", "7z", "gz", "tgz", "bz2", "xz", "jar", "war", "apk",
|
||||||
|
"docx", "xlsx", "pptx", "odt", "ods", "odp", "vsdx",
|
||||||
|
"jpg", "jpeg", "png", "gif", "webp",
|
||||||
|
"mp4", "avi", "mkv", "mov", "wmv", "flv", "mpg", "mpeg", "webm",
|
||||||
|
"mp3", "aac", "flac", "ogg");
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private UserFileService userFileService;
|
private UserFileService userFileService;
|
||||||
|
|
||||||
@@ -139,6 +151,9 @@ public class TransferUserFileServiceImpl implements TransferUserFileService {
|
|||||||
|
|
||||||
// 创建 ZipEntry(使用 userFileId 命名)
|
// 创建 ZipEntry(使用 userFileId 命名)
|
||||||
String entryName = folderName + "/" + userFileId;
|
String entryName = folderName + "/" + userFileId;
|
||||||
|
zos.setLevel(isPreCompressed(file.getFileName())
|
||||||
|
? Deflater.NO_COMPRESSION
|
||||||
|
: Deflater.DEFAULT_COMPRESSION);
|
||||||
ZipEntry zipEntry = new ZipEntry(entryName);
|
ZipEntry zipEntry = new ZipEntry(entryName);
|
||||||
zos.putNextEntry(zipEntry);
|
zos.putNextEntry(zipEntry);
|
||||||
|
|
||||||
@@ -166,6 +181,8 @@ public class TransferUserFileServiceImpl implements TransferUserFileService {
|
|||||||
|
|
||||||
String metadataJson = JsonUtils.toJsonString(metadata);
|
String metadataJson = JsonUtils.toJsonString(metadata);
|
||||||
ZipEntry metadataEntry = new ZipEntry("transferMetadata.json");
|
ZipEntry metadataEntry = new ZipEntry("transferMetadata.json");
|
||||||
|
// 上一个文件可能把 level 置成了 0,JSON 元数据可压缩,需还原
|
||||||
|
zos.setLevel(Deflater.DEFAULT_COMPRESSION);
|
||||||
zos.putNextEntry(metadataEntry);
|
zos.putNextEntry(metadataEntry);
|
||||||
zos.write(metadataJson.getBytes("UTF-8"));
|
zos.write(metadataJson.getBytes("UTF-8"));
|
||||||
zos.closeEntry();
|
zos.closeEntry();
|
||||||
@@ -173,4 +190,15 @@ public class TransferUserFileServiceImpl implements TransferUserFileService {
|
|||||||
|
|
||||||
log.info("传输文件打包完成: {}", finalFilePath);
|
log.info("传输文件打包完成: {}", finalFilePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isPreCompressed(String fileName) {
|
||||||
|
if (fileName == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int dot = fileName.lastIndexOf('.');
|
||||||
|
if (dot < 0 || dot == fileName.length() - 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return PRE_COMPRESSED_SUFFIXES.contains(fileName.substring(dot + 1).toLowerCase(Locale.ROOT));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user