学以致用!
线程 & 线程池 & 文件I/O流 & Zip文件压缩无需其他特别依赖。
功能:
多线程完成批量文件(夹)(xls,txt,doc…)生成 & 压缩 & 下载(副本生成)
直接上源码:
1、程序入口(开启一个生成 & 压缩文件线程 & 生成zip副本)
import com.dongzi.GenerateZipFileThread;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) throws InterruptedException, IOException {
String fileDirPath = "D:/dongzi";
String zipFileName = "D:/dongzi.zip";
Map<String, Object> params = new HashMap<>();
params.put("generateCount", 5);
params.put("fileName", "新建文件");
params.put("content", "文本内容");
GenerateZipFileThread zipFileThread = new GenerateZipFileThread(fileDirPath, zipFileName, params);
Thread thread = new Thread(zipFileThread);
thread.start();
thread.join(); // 等待线程执行完毕,直接生成一个zip压缩文件的副本
downloadZip(zipFileName, "D:/dongzi-副本.zip");
}
/**
* 读 zipFileName 压缩文件流 到 toZipFileName压缩文件中
*
* @param zipFileName 源文件
* @param toZipFileName 目标文件
*/
public static void downloadZip(String zipFileName, String toZipFileName) throws IOException {
FileInputStream zipInStream = new FileInputStream(zipFileName);
FileOutputStream toZipOutStream = new FileOutputStream(toZipFileName);
byte[] b = new byte[1024];
while ((zipInStream.read(b)) > -1) {
toZipOutStream.write(b);
}
toZipOutStream.flush(); // 还可以将文件流刷新到其他地方
// 切记关流
toZipOutStream.close();
zipInStream.close();
}
}
2、开启线程生成文件 & 生成的文件进行zip压缩
package com.dongzi;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* 生成 ZIP 压缩文件的线程
* <pre>
* 为什么实现Runnable?而不是继承Thread?
* 在主进程中继承Thread的线程只能进行一次start(),
* 调用第二次会出现异常(java.lang.IllegalStateException),而实现Runnable不会出现
* </pre>
*/
public class GenerateZipFileThread implements Runnable {
/**
* 需要被压缩的源文件夹路径。eg: D:/dongzi
*/
private final String fileDirPath;
/**
* 需要压缩成zip的文件名称。eg:D:/dongzi.zip
*/
private final String zipFileName;
/**
* 参数:可自定
*/
private final Map<String, Object> params;
private final ExecutorService executorService;
public GenerateZipFileThread(String fileDirPath, String zipFileName, Map<String, Object> params) {
this.fileDirPath = fileDirPath;
this.zipFileName = zipFileName;
this.params = params;
this.executorService = Executors.newFixedThreadPool(5);
}
/**
* 重载 Runnable的run() 方法
*/
@Override
public void run() {
if (params != null) {
Integer generateCount = (Integer) params.get("generateCount"); // 假定需要生成到 fileDirPath 文件夹中的文件数量
List<Future<String>> futureList = new ArrayList<>();
for (int i = 0; i < generateCount; i++) {
/*
* 为什么匿名实现Callable而不是 Runnable,因为Callable线程执行完毕后能返回执行结果
*/
int cnt = i;
Future<String> future = executorService.submit(new Callable<String>() {
@Override
public String call() throws Exception {
// 文件夹
File fileDir = new File(fileDirPath);
// 文件夹不存在
if (!fileDir.exists()) {
// 创建新文件夹
boolean mkdir = fileDir.mkdir();
if (mkdir) {
System.out.println("already create new folder --> " + fileDir.getName());
}
}
// 需要在文件夹中创建的文件
String name = (String) params.get("fileName");
// 文件名
String fileName = name + "-" + cnt + ".txt";
// 文件生成到的路径. eg: D:/dongzi/文件-1.txt
String genFileName = fileDirPath + "/" + fileName;
File file = new File(genFileName);
// 需要被生成的文件已经存在
if (file.exists()) {
boolean delete = file.delete();
if (delete) {
System.out.println(file.getName() + " --> already exists. delete!");
}
}
// 文件内容
String content = (String) params.get("content");
FileOutputStream out = new FileOutputStream(file);
PrintWriter printWriter = new PrintWriter(out);
// 向文本写入内容
printWriter.write(content + "----" + cnt);
printWriter.flush();
printWriter.close();
out.close();
// or todo other service
// ----- 至此 文件已经生成到对应文件夹
return null;
}
});
// 每提交一次线程
futureList.add(future);
}
try {
/*
* 线程全部提交完成之后,检查线程是否执行完毕:
* 执行完毕后将 fileDirPath 的文件夹 压缩成 zipFileName文件
*/
checkStatus(futureList, fileDirPath, zipFileName);
} catch (InterruptedException | IOException e) {
throw new RuntimeException(e);
}
}
}
/**
* @param futureList
* @param fileDirPath
* @param zipFileName
*/
private void checkStatus(List<Future<String>> futureList, String fileDirPath, String zipFileName) throws InterruptedException, IOException {
List<Future<String>> futures = new ArrayList<>();
for (Future<String> future : futureList) {
if (future.isDone()) {
// 线程执行完成
// future.get(); 客户获取线程执行结果
} else {
// 等待一段时间重新检查线程执行状态
Thread.sleep(200);
futures.add(future);
}
}
if (!futures.isEmpty()) {
checkStatus(futureList, fileDirPath, zipFileName);
} else {
// 线程全部执行完毕:将文件压缩
// 需要压缩的源文件夹
File sourceFileFolder = new File(fileDirPath);
File zipFile = new File(zipFileName);
FileOutputStream out = new FileOutputStream(zipFile);
ZipOutputStream zipOutputStream = new ZipOutputStream(out);
fileToZip(sourceFileFolder, sourceFileFolder.getName(), zipOutputStream);
// 切记关流:如果不关流可能导致文件压缩损坏
zipOutputStream.close();
out.close();
// todo: other
}
}
/**
* @param sourceFile 需要压缩的源文件夹
* @param fileName 压缩文件名
* @param zipOut 压缩文件输出流
*/
public void fileToZip(File sourceFile, String fileName, ZipOutputStream zipOut) throws IOException {
if (sourceFile.isDirectory()) {
if (fileName.endsWith("/")) {
zipOut.putNextEntry(new ZipEntry(fileName));
zipOut.closeEntry();
} else {
zipOut.putNextEntry(new ZipEntry(fileName + "/"));
zipOut.closeEntry();
}
File[] childFiles = sourceFile.listFiles();
// 遍历所有子文件
assert childFiles != null;
for (File childFile : childFiles) {
fileToZip(childFile, fileName + "/" + childFile.getName(), zipOut);
}
return;
}
// ...
// 读文件,将文件压缩到zip文件中去
FileInputStream fis = new FileInputStream(sourceFile);
ZipEntry zipEntry = new ZipEntry(fileName);
zipOut.putNextEntry(zipEntry);
byte[] bytes = new byte[1024];
int length;
while ((length = fis.read(bytes)) >= 0) {
zipOut.write(bytes, 0, length);
}
zipOut.flush();
fis.close();
if (this.executorService != null) {
executorService.shutdown();
}
}
}
3、执行结果展示
参考链接: