基于POI、zip4j 给word、pdf、excel及zip加密
需求说明:
许多人希望能够对自己的Office文件以及压缩的ZIP文件进行加密,以防止未经授权的访问和信息泄露。然而,使用WPS、Microsoft Office软件或其它压缩工具逐一对每一个文件进行加密,实在是耗时且费力,往往让人感到疲惫与沮丧。
因此,本文将详细介绍一种更为高效的解决方案,以免大家在人力和时间上面无谓的消耗。本文的主要内容结构包括以下几个部分:
1、所需依赖
2、文档类加密工具类 + 测试示例
3、zip包加密工具类 + 测试示例
效果如下:
1、所需依赖:
提前声明(JAR包版本已经经过筛选 尽量不用对依赖做变更,不然部分方法或者类不存在)且注意各个POI依赖之间的版本信息。
<!--其他包 与主题无关 为保完整性故完整列出 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.21</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
<scope>compile</scope>
</dependency>
<!-- office加密所需 -->
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
<version>2.0.27</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext7-core</artifactId>
<version>7.1.11</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
</dependency>
<!-- Apache POI -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.2.3</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.3</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>org.apache.xmlbeans</groupId>
<artifactId>xmlbeans</artifactId>
<version>5.1.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.4</version>
</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>fr.opensagres.xdocreport.converter.docx.xwpf</artifactId>
<version>1.0.6</version>
</dependency>
<!--ZIP包加密 所需 -->
<dependency>
<groupId>net.lingala.zip4j</groupId>
<artifactId>zip4j</artifactId>
<version>2.9.1</version>
</dependency>
2、文档加密方法工具类:
package com.example.demo;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.encryption.StandardProtectionPolicy;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.poifs.crypt.EncryptionInfo;
import org.apache.poi.poifs.crypt.EncryptionMode;
import org.apache.poi.poifs.crypt.Encryptor;
import org.apache.poi.poifs.crypt.standard.StandardEncryptor;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import java.io.*;
import java.nio.file.Files;
/**
* 类描述:文件加密工具类 (word、excel、pdf )
* 如有新增 请补充到描述中。
*
* @author xú
* @since 1.0.0 2024-08-27 04:45:19
*/
public class DocumentEncryptionUtil {
private DocumentEncryptionUtil() {
}
/**
* 方法描述:加密word文件
*
* @param file 文件
* @param password 密码
* @author xú
* @since 1.0.0 2024-08-27 04:46:54
*/
public static void encryptWordFile(File file, String password) {
try (POIFSFileSystem fs = new POIFSFileSystem();
FileInputStream fis = new FileInputStream(file);
XWPFDocument document = new XWPFDocument(fis)) {
EncryptionInfo info = new EncryptionInfo(EncryptionMode.agile);
Encryptor encryptor = info.getEncryptor();
encryptor.confirmPassword(password);
try (OutputStream os = encryptor.getDataStream(fs)) {
document.write(os);
}
try (FileOutputStream fos = new FileOutputStream(file)) {
fs.writeFilesystem(fos);
}
//System.out.println("Encrypted Word file: " + file.getAbsolutePath());
} catch (org.apache.pdfbox.pdmodel.encryption.InvalidPasswordException e) {
//System.out.println("已加密");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 方法描述:加密excel-xlsx
*
* @param file 文件
* @param password 密码
* @author xú
* @since 1.0.0 2024-08-27 05:10:51
*/
public static void encryptExcelXlsx(File file, String password) {
try (POIFSFileSystem fs = new POIFSFileSystem();
FileInputStream fis = new FileInputStream(file);
XSSFWorkbook workbook = new XSSFWorkbook(fis)) {
EncryptionInfo info = new EncryptionInfo(EncryptionMode.agile);
Encryptor encryptor = info.getEncryptor();
encryptor.confirmPassword(password);
try (OutputStream os = encryptor.getDataStream(fs)) {
workbook.write(os);
}
try (FileOutputStream fos = new FileOutputStream(file)) {
fs.writeFilesystem(fos);
}
//System.out.println("Encrypted Excel file: " + file.getAbsolutePath());
} catch (org.apache.pdfbox.pdmodel.encryption.InvalidPasswordException e) {
//System.out.println("已加密");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 方法描述:加密excel-xls
*
* @param file 文件
* @param password 密码
* @author xú
* @since 1.0.0 2024-08-27 05:10:45
*/
public static void encryptExcelXls(File file, String password) {
try (POIFSFileSystem fs = new POIFSFileSystem(); FileInputStream fis = new FileInputStream(file);
HSSFWorkbook workbook = new HSSFWorkbook(fis)) {
EncryptionInfo info = new EncryptionInfo(EncryptionMode.standard);
StandardEncryptor encryptor = (StandardEncryptor) info.getEncryptor();
encryptor.confirmPassword(password);
try (OutputStream os = encryptor.getDataStream(fs)) {
workbook.write(os);
}
try (FileOutputStream fos = new FileOutputStream(file)) {
fs.writeFilesystem(fos);
}
System.out.println("Encrypted Excel file: " + file.getAbsolutePath());
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 方法描述:加密pdf文件
*
* @param file 文件
* @param password 密码
* @author xú
* @since 1.0.0 2024-08-27 04:46:05
*/
public static void encryptPdfFile(File file, String password) {
try (PDDocument document = PDDocument.load(file)) {
// Set the encryption options
StandardProtectionPolicy policy = new StandardProtectionPolicy(password, password, new org.apache.pdfbox.pdmodel.encryption.AccessPermission());
policy.setEncryptionKeyLength(128); // You can set the key length to 128 or 256 bits
document.protect(policy);
document.save(file);
//System.out.println("Encrypted PDF file: " + file.getAbsolutePath());
} catch (org.apache.pdfbox.pdmodel.encryption.InvalidPasswordException e) {
//System.out.println("已加密");
} catch (Exception e) {
e.printStackTrace();
}
}
}
测试文档加密工具类
3、测试及测试结果展示:
public static void main(String[] args) {
String directoryPath = "D:\\桌面\\项目文档"; // 替换为你本地的路径
String backupDirectoryPath = "D:\\桌面\\备份"; // 替换为你的备份路径
String password = "aaaaa111112DDD"; // 密码设置
try {
Files.createDirectories(Paths.get(backupDirectoryPath));
Files.walkFileTree(Paths.get(directoryPath), new SimpleFileVisitor<>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
String fileName = file.getFileName().toString().toLowerCase();
if (fileName.endsWith(".docx") || fileName.endsWith(".xlsx") || fileName.endsWith(".pdf")) {
Path backupFile = Paths.get(backupDirectoryPath, file.toString().substring(directoryPath.length()));
Files.createDirectories(backupFile.getParent());
Files.copy(file, backupFile, StandardCopyOption.REPLACE_EXISTING);
try {
if (fileName.endsWith(".docx")) {
encryptWordFile(file.toFile(), password);
} else if (fileName.endsWith(".xlsx")) {
encryptExcelFile(file.toFile(), password);
} else if (fileName.endsWith(".pdf")) {
encryptPdfFile(file.toFile(), password);
}
}catch (Exception ex){
ex.printStackTrace();
}
System.out.println("Processed file: " + file.toString());
} else {
System.out.println("Skipped file: " + file.toString());
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
Path backupDir = Paths.get(backupDirectoryPath, dir.toString().substring(directoryPath.length()));
Files.createDirectories(backupDir);
System.out.println("Directory: " + dir.toString());
return FileVisitResult.CONTINUE;
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
4、 zip包加密工具类
package com.example.demo;
import net.lingala.zip4j.ZipFile;
import net.lingala.zip4j.model.FileHeader;
import net.lingala.zip4j.model.ZipParameters;
import net.lingala.zip4j.model.enums.CompressionLevel;
import net.lingala.zip4j.model.enums.CompressionMethod;
import net.lingala.zip4j.model.enums.EncryptionMethod;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* 类描述:zip包加密工具类
*
* @author xú chūn chéng
* @version 1.0.0
* @since 1.0.0 2024-08-27 04:49:55
*/
public class ZipEncryptionUtil {
private ZipEncryptionUtil() {
}
/**
* 方法描述:使用旧zip创建加密zip
*
* @param oldZipFile 需要加密的zip包文件
* @param password 密码
* @author xú
* @since 1.0.0 2024-08-27 04:49:50
*/
public static void createEncryptedZipWithOldZip(File oldZipFile, String password) {
//创建新的加密zip文件
String newZipFileName = oldZipFile.getParent() + File.separator + "encrypted_" + oldZipFile.getName();
try (ZipFile newEncryptedZipFile = new ZipFile(newZipFileName, password.toCharArray())) {
//设置加密的zip参数
ZipParameters zipParameters = new ZipParameters();
zipParameters.setCompressionMethod(CompressionMethod.DEFLATE);
zipParameters.setCompressionLevel(CompressionLevel.NORMAL);
zipParameters.setEncryptFiles(true);
zipParameters.setEncryptionMethod(EncryptionMethod.AES);
// 将旧的zip文件添加到新的加密zip文件中
newEncryptedZipFile.addFile(oldZipFile, zipParameters);
} catch (IOException e) {
throw new RuntimeException(e);
}
//System.out.println("Created encrypted ZIP file: " + newZipFileName);
}
/**
* 方法描述:加密zip文件 (会把OLD压缩文件解压开 然后重新打包加密) 慎用
* ( 这种加密方式存在问题 当文件内部多层级的时候无法正常加密 且中文乱码)
*
* @param file 文件
* @param password 密码
* @author xú chūn chéng
* @since 1.0.0 2024-08-27 04:48:46
*/
public static void encryptZipFile(File file, String password) {
try {
File tempDir;
try (ZipFile zipFile = new ZipFile(file, password.toCharArray())) {
// 设置压缩文件
ZipParameters zipParameters = new ZipParameters();
zipParameters.setCompressionMethod(CompressionMethod.DEFLATE);
zipParameters.setCompressionLevel(CompressionLevel.NORMAL);
zipParameters.setEncryptFiles(true);
zipParameters.setEncryptionMethod(EncryptionMethod.AES);
//将所有文件解压缩到临时目录
tempDir = Files.createTempDirectory("zip4j").toFile();
zipFile.extractAll(tempDir.getAbsolutePath());
// 从zip中删除所有文件
List<FileHeader> fileHeaders = zipFile.getFileHeaders();
List<FileHeader> headersToRemove = new ArrayList<>(fileHeaders); // 将要删除的文件头收集到一个新列表中
for (FileHeader fileHeader : headersToRemove) {
zipFile.removeFile(fileHeader);
}
//使用加密重新打包文件
for (File extractedFile : Objects.requireNonNull(tempDir.listFiles())) {
zipFile.addFile(extractedFile, zipParameters);
}
}
//清理临时目录
for (File extractedFile : Objects.requireNonNull(tempDir.listFiles())) {
extractedFile.delete();
}
tempDir.delete();
//System.out.println("Encrypted ZIP file: " + file.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
}
}
5、ZIP 方式1测试:
public static void main(String[] args) {
String directoryPath = "D:\\桌面\\新建文件夹\\已压缩_未归类"; // 替换为你本地的路径
String backupDirectoryPath = "D:\\桌面\\备份"; // 替换为你的备份路径
String password = "741841aaa333.";//密码
long maxSize = 1024L * 1024L * 1024L; // 1GB
try {
Files.createDirectories(Paths.get(backupDirectoryPath));
// 首先收集所有要处理的文件
List<Path> filesToProcess = new ArrayList<>();
Files.walkFileTree(Paths.get(directoryPath), new SimpleFileVisitor<>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
String fileName = file.getFileName().toString().toLowerCase();
//由于超过1G的文件处理时间过长 所需对此文件大小进行限制 最大为1GB
if (fileName.endsWith(".zip") && Files.size(file) <= maxSize) {
filesToProcess.add(file);
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
Path backupDir = Paths.get(backupDirectoryPath, dir.toString().substring(directoryPath.length()));
Files.createDirectories(backupDir);
return FileVisitResult.CONTINUE;
}
});
// 然后处理收集到的文件
int totalFiles = filesToProcess.size();
for (int i = 0; i < totalFiles; i++) {
Path file = filesToProcess.get(i);
Path backupFile = Paths.get(backupDirectoryPath, file.toString().substring(directoryPath.length()));
Files.createDirectories(backupFile.getParent());
Files.copy(file, backupFile, StandardCopyOption.REPLACE_EXISTING);
ZipEncryptionUtil.encryptZipFile(file.toFile(), password);
// 记录进度
System.out.printf("Processed file (%d/%d): %s%n", i + 1, totalFiles, file.toString());
}
} catch (IOException e) {
e.printStackTrace();
}
}
6:zip2加密方式测试 :
需要创建一个新的 ZIP 文件,并将原有的 ZIP 文件(OLD 压缩文件)作为一个单独的文件放入新 ZIP 文件中,同时对新 ZIP 文件进行加密。
方法测试:
public static void main(String[] args) {
String directoryPath = "D:\\桌面\\AAA"; // 替换为你本地的路径
String backupDirectoryPath = "D:\\桌面\\备份"; // 替换为你的备份路径
String password = "ssssssss.";
long maxSize = 1024L * 1024L * 1024L; // 1GB
try {
Files.createDirectories(Paths.get(backupDirectoryPath));
// 首先收集所有要处理的文件
List<Path> filesToProcess = new ArrayList<>();
Files.walkFileTree(Paths.get(directoryPath), new SimpleFileVisitor<>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
String fileName = file.getFileName().toString().toLowerCase();
if (fileName.endsWith(".zip") && Files.size(file) <= maxSize) {
filesToProcess.add(file);
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
Path backupDir = Paths.get(backupDirectoryPath, dir.toString().substring(directoryPath.length()));
Files.createDirectories(backupDir);
return FileVisitResult.CONTINUE;
}
});
// 然后处理收集到的文件
int totalFiles = filesToProcess.size();
for (int i = 0; i < totalFiles; i++) {
Path file = filesToProcess.get(i);
Path backupFile = Paths.get(backupDirectoryPath, file.toString().substring(directoryPath.length()));
Files.createDirectories(backupFile.getParent());
Files.copy(file, backupFile, StandardCopyOption.REPLACE_EXISTING);
ZipEncryptionUtil.createEncryptedZipWithOldZip(file.toFile(), password);
// 记录进度
System.out.printf("Processed file (%d/%d): %s%n", i + 1, totalFiles, file.toString());
}
} catch (IOException e) {
e.printStackTrace();
}
}