把要压缩的文件保存到同一个文件夹下,然后使用ZipUtils对该文件夹打包,然后把数据流写入响应体中返回给前端下载即可
public void downloadBatchWord(InspectLogQueryDto queryDto, HttpServletResponse response) {
if(ObjectUtils.isNotEmpty(queryDto.getIds())&&queryDto.getIds().size()==1){
queryDto.setId(queryDto.getIds().get(0));
downloadSingleWord(queryDto, response);
}
List<InspectLog> inspectLogs = inspectLogMapper.selectList(queryDto);
List<String> fileNames = new ArrayList<>();
try {
if(ObjectUtils.isNotEmpty(inspectLogs)){
for (int i = 0; i < inspectLogs.size(); i++) {
InspectLog log = inspectLogs.get(i);
Map<String, Object> replaceMap = dataFilling(log);
OutputStream outputStream = null;
if (replaceMap != null) {
//把生成的word存到临时文件夹中
// 生成Word文档的临时路径
String year = replaceMap.get("year").toString();
String month = String.valueOf((Integer.valueOf(replaceMap.get("month").toString())+1));
String day = replaceMap.get("day").toString();
String s = year+"年"+month+"月"+day+"日";
String index = new DecimalFormat("000").format(i+1);
String fileName = "巡查日志"+s+"-"+index+".docx";
String docPath = System.getProperty("java.io.tmpdir") + "/" + fileName;
outputStream = new FileOutputStream(docPath);
// 加载Word模板并填充数据
Resource resource = resourceLoader.getResource("classpath:word/xcrz-template.docx");
InputStream tempFile = resource.getInputStream();
WordUtils.toWord(tempFile, replaceMap, outputStream);
outputStream.flush();
fileNames.add(fileName);
}
}
response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
response.addHeader("Content-type", "application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("巡查日志.zip", "UTF-8"));
response.setCharacterEncoding("UTF-8");
downloadObject(fileNames,response.getOutputStream());
}
} catch (Exception e) {
e.printStackTrace();
}
}
WordUtils
package com.ztjz.itp.hams.business.util;
import com.deepoove.poi.XWPFTemplate;
import com.forest.support.base.result.ItemCount;
import org.apache.poi.xwpf.usermodel.*;
import org.docx4j.jaxb.Context;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.PartName;
import org.docx4j.openpackaging.parts.WordprocessingML.AlternativeFormatInputPart;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.docx4j.org.apache.poi.util.IOUtils;
import org.docx4j.relationships.Relationship;
import org.docx4j.wml.CTAltChunk;
import org.springframework.util.ClassUtils;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* @author HelloWord高级工程师
* @describe:
* @date 2023年03月24日 下午 03:48
*/
public class WordUtils {
/**
* 生成word
*
* @param tempfile
* @param data
* @param outfile
* @return
*/
public static boolean toWord(InputStream tempfile, Map<String, Object> data, String outfile) {
return toWord(tempfile, data, new File(outfile));
}
/**
* 生成word
*
* @param tempfile
* @param data
* @param outputStream
* @return
*/
public static boolean toWord(InputStream tempfile, Map<String, Object> data, OutputStream outputStream) {
boolean result = false;
XWPFTemplate template = null;
try {
template = XWPFTemplate.compile(tempfile).render(data);
template.write(outputStream);
outputStream.flush();
result = true;
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (outputStream != null) {
outputStream.close();
}
if (template != null) {
template.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
/**
* 生成word
*
* @param tempfile
* @param data
* @param outfile
* @return
*/
public static boolean toWord(InputStream tempfile, Map<String, Object> data, File outfile) {
boolean result = false;
FileOutputStream out = null;
XWPFTemplate template = null;
try {
template = XWPFTemplate.compile(tempfile).render(data);
out = new FileOutputStream(outfile);
template.write(out);
out.flush();
result = true;
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (out != null) {
out.close();
template.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
public static void mergeDocxByFile(List<File> list, String path) {
List<InputStream> inList = new ArrayList<InputStream>();
for (int i = 0; i < list.size(); i++)
try {
inList.add(new FileInputStream(list.get(i)));
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
InputStream inputStream = mergeDocx(inList);
saveTemplate(inputStream, path);
} catch (Docx4JException | IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static InputStream mergeDocx(final List<InputStream> streams)
throws Docx4JException, IOException {
WordprocessingMLPackage target = null;
final File generated = File.createTempFile("generated", ".docx");
int chunkId = 0;
Iterator<InputStream> it = streams.iterator();
while (it.hasNext()) {
InputStream is = it.next();
if (is != null) {
if (target == null) {
// Copy first (master) document
OutputStream os = new FileOutputStream(generated);
os.write(IOUtils.toByteArray(is));
os.close();
target = WordprocessingMLPackage.load(generated);
} else {
// Attach the others (Alternative input parts)
insertDocx(target.getMainDocumentPart(),
IOUtils.toByteArray(is), chunkId++);
}
}
}
if (target != null) {
target.save(generated);
return new FileInputStream(generated);
} else {
return null;
}
}
// 插入文档
private static void insertDocx(MainDocumentPart main, byte[] bytes, int chunkId) {
try {
AlternativeFormatInputPart afiPart = new AlternativeFormatInputPart(new PartName("/part" + chunkId + ".docx"));
//afiPart.setContentType(new ContentType(CONTENT_TYPE));
afiPart.setBinaryData(bytes);
Relationship altChunkRel = main.addTargetPart(afiPart);
CTAltChunk chunk = Context.getWmlObjectFactory().createCTAltChunk();
chunk.setId(altChunkRel.getId());
main.addObject(chunk);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void saveTemplate(InputStream fis, String toDocPath) {
FileOutputStream fos;
int bytesum = 0;
int byteread = 0;
try {
fos = new FileOutputStream(toDocPath);
byte[] buffer = new byte[1444];
while ((byteread = fis.read(buffer)) != -1) {
bytesum += byteread; //字节数 文件大小
fos.write(buffer, 0, byteread);
}
fis.close();
fos.close();
} catch (FileNotFoundException e1) {
e1.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public static InputStream readFile(String fileName) {
InputStream inputStream = ClassUtils.class.getClassLoader().getResourceAsStream("templates/word/" + fileName + ".docx");
return inputStream;
}
public synchronized static void download(HttpServletResponse response, File file) {
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
response.setContentType("application/octet-stream");
// 下载文件能正常显示中文
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(file.getName(), "UTF-8"));
bis = new BufferedInputStream(new FileInputStream(file));
bos = new BufferedOutputStream(response.getOutputStream());
byte[] buff = new byte[10240];
int bytesRead;
while (-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
bos.write(buff, 0, bytesRead);
}
bis.close();
bos.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
//这里为测试代码,可以自行封装
String fileName = "D:\\test.docx";
File f = new File(fileName);
if (f.exists()) {
f.delete();
}
}
}
/**
* WordUtils.exportDataToWord(totalList, tempFile, replaceMap, outputStream, 3, 1);
*
* totalList 源数据
* tempFile 是临时文件输入流,此时临时文件是空的
* replaceMap 是替换word模板中的数据
* outputStream 写数据
*
*/
public static boolean exportDataToWord(List<List<ItemCount>> totalList, InputStream templateFile, Map<String, Object> replaceMap, OutputStream outputStream, int tableIndex, int startRow) throws IOException {
boolean result = false;
XWPFTemplate template = null;
try {
template = XWPFTemplate.compile(templateFile).render(replaceMap);
XWPFDocument document = template.getXWPFDocument();
int currentTableIndex = tableIndex; // 保存当前表格的索引
for (int j = 0; j < totalList.size(); j++) {
List<ItemCount> itemList = totalList.get(j);
// 获取指定索引的表格
XWPFTable table = document.getTables().get(currentTableIndex);
// 获取指定索引的起始行
XWPFTableRow startTableRow = table.getRow(startRow);
//获取表格行高
int height = startTableRow.getHeight();
// 填充数据
for (int i = 0; i < itemList.size(); i++) {
ItemCount item = itemList.get(i);
XWPFTableRow dataRow;
if (i >= table.getNumberOfRows() - startRow) {
// 如果索引超过或等于剩余行数,创建新的行,新行行高和原表行高一样
dataRow = table.createRow();
dataRow.setHeight(height);
} else {
// 否则获取指定索引的行
dataRow = table.getRow(startRow + i);
}
List<XWPFTableCell> cells = dataRow.getTableCells();
// 填充 表格第一列 字段
XWPFTableCell cell = cells.get(0);
cell.setText(item.getIndex());
cell.getParagraphs().get(0).setAlignment(ParagraphAlignment.CENTER);
// 填充 表格第二列 字段
cell = cells.get(1);
cell.setText(item.getLabel());
cell.getParagraphs().get(0).setAlignment(ParagraphAlignment.CENTER);
// 填充 表格第三列 字段
cell = cells.get(2);
cell.setText(String.valueOf(item.getCount()));
cell.getParagraphs().get(0).setAlignment(ParagraphAlignment.CENTER);
// 填充 表格第四列 字段
if (4 <= cells.size()) {
cell = cells.get(3);
cell.setText(String.valueOf(item.getCode()));
cell.getParagraphs().get(0).setAlignment(ParagraphAlignment.CENTER);
}
}
currentTableIndex++; // 更新当前表格的索引
}
// 写入输出流
document.write(outputStream);
outputStream.flush();
outputStream.close();
result = true;
} catch (IOException e) {
e.printStackTrace();
} finally {
if (template != null) {
template.close();
}
}
return result;
}
/**
* 所传totalList集合中对象超过四个元素情况
*
* @param totalList
* @param templateFile
* @param replaceMap
* @param outputStream
* @param tableIndex
* @param startRow
* @return
* @throws IOException
*/
public static boolean exportDataToWord(List<List<Word>> totalList, Boolean isContainMoreThanFourElement, InputStream templateFile, Map<String, Object> replaceMap, OutputStream outputStream, int tableIndex, int startRow) throws IOException {
boolean result = false;
XWPFTemplate template = null;
try {
template = XWPFTemplate.compile(templateFile).render(replaceMap);
XWPFDocument document = template.getXWPFDocument();
int currentTableIndex = tableIndex; // 保存当前表格的索引
for (int j = 0; j < totalList.size(); j++) {
List<Word> itemList = totalList.get(j);
// 获取指定索引的表格
XWPFTable table = document.getTables().get(currentTableIndex);
// 获取指定索引的起始行
XWPFTableRow startTableRow = table.getRow(startRow);
//获取表格行高
int height = startTableRow.getHeight();
// 填充数据
for (int i = 0; i < itemList.size(); i++) {
Word item = itemList.get(i);
XWPFTableRow dataRow;
if (i >= table.getNumberOfRows() - startRow) {
// 如果索引超过或等于剩余行数,创建新的行,新行行高和原表行高一样
dataRow = table.createRow();
dataRow.setHeight(height);
} else {
// 否则获取指定索引的行
dataRow = table.getRow(startRow + i);
}
List<XWPFTableCell> cells = dataRow.getTableCells();
// 填充 表格第一列 字段
XWPFTableCell cell = cells.get(0);
cell.setText(item.getName1());
cell.getParagraphs().get(0).setAlignment(ParagraphAlignment.CENTER);
// 填充 表格第二列 字段
cell = cells.get(1);
cell.setText(item.getName2());
cell.getParagraphs().get(0).setAlignment(ParagraphAlignment.CENTER);
// 填充 表格第三列 字段
cell = cells.get(2);
cell.setText(String.valueOf(item.getName3()));
cell.getParagraphs().get(0).setAlignment(ParagraphAlignment.CENTER);
// 填充 表格第四列 字段
if (4 <= cells.size()) {
cell = cells.get(3);
cell.setText(String.valueOf(item.getName4()));
cell.getParagraphs().get(0).setAlignment(ParagraphAlignment.CENTER);
}
// 填充 表格第五列 字段
if (5 <= cells.size()) {
cell = cells.get(4);
cell.setText(String.valueOf(item.getName5()));
cell.getParagraphs().get(0).setAlignment(ParagraphAlignment.CENTER);
}
// 填充 表格第六列 字段
if (6 <= cells.size()) {
cell = cells.get(5);
cell.setText(String.valueOf(item.getName6()));
cell.getParagraphs().get(0).setAlignment(ParagraphAlignment.CENTER);
}
}
currentTableIndex++; // 更新当前表格的索引
}
// 写入输出流
document.write(outputStream);
outputStream.flush();
outputStream.close();
result = true;
} catch (IOException e) {
e.printStackTrace();
} finally {
if (template != null) {
template.close();
}
}
return result;
}
}
ZipUtils
package com.ztjz.itp.hams.business.util;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ZipUtil;
import java.io.*;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* 通过Java的Zip输入输出流实现压缩和解压文件
*
* @author HelloWord高级工程师
*
*/
public final class ZipUtils extends ZipUtil{
private static final Charset DEFAULT_CHARSET = CharsetUtil.defaultCharset();
/**
*
* 生成Zip压缩包 (注意是对hutool的二次封装,所以必须要有hutool依赖)
*
* @param zipFile 要生成的目标zip压缩包
* @param sourceFiles 压缩包中包含的文件集合
* @param dirWithFlag 是否将文件目录一同打包进去 (true:压缩包中包含文件目录,false:压缩包中不包含目录)
* @author liukai
*/
public static File zip(File zipFile, List<File> sourceFiles, boolean dirWithFlag) {
File file = null;
if (CollUtil.isNotEmpty(sourceFiles)) {
File[] fileArr = sourceFiles.toArray(new File[]{});
file = zip(zipFile, dirWithFlag, fileArr);
}
return file;
}
/**
*
* 生成Zip压缩包文件 (注意是对hutool的二次封装,所以必须要有hutool依赖)
*
* @param zipFile 要生成的目标zip压缩包路径
* @param fileNames 待压缩文件路径名称
* @param contents 待压缩文件输入流
* @author kemi
*/
public static void zip(File zipFile, List<String> fileNames, List<InputStream> contents) {
FileOutputStream fos = null;
try {
fos = new FileOutputStream(zipFile);
zip(fos, fileNames, contents);
}catch (Exception e){
e.printStackTrace();
}finally {
IoUtil.close(fos);
}
}
/**
*
* 生成Zip压缩二进制包 (注意是对hutool的二次封装,所以必须要有hutool依赖)
*
* @param fileNames 待压缩文件路径名称
* @param contents 待压缩文件输入流
* @author kemi
*/
public static void zip(OutputStream outputStream, List<String> fileNames, List<InputStream> contents) {
ZipOutputStream zos = null;
if (CollUtil.isNotEmpty(fileNames) && CollUtil.isNotEmpty(contents)) {
if (fileNames.size() != contents.size()) {
throw new IllegalArgumentException("fileNames length is not equals to contents length !");
} else {
try {
zos = getZipOutputStream(outputStream, DEFAULT_CHARSET);
ZipEntry zipEntry;
InputStream ios;
for(int i = 0, size = fileNames.size(); i< size; i++) {
zipEntry = new ZipEntry(fileNames.get(i));
zos.putNextEntry(zipEntry);
ios = contents.get(i);
IOUtils.copy(ios, zos);
IoUtil.close(ios);
zos.closeEntry();
}
zos.finish();;
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
IoUtil.close(zos);
}
}
} else {
throw new IllegalArgumentException("fileNames or contents is empty !");
}
}
/**
*
* 生成Zip压缩二进制包 (注意是对hutool的二次封装,所以必须要有hutool依赖)
*
* @param fileNames 待压缩文件路径名称
* @param contents 待压缩文件输入流
* @author kemi
*/
public static byte[] zip(List<String> fileNames, List<InputStream> contents) {
ByteArrayOutputStream bos = null;
try {
bos = new ByteArrayOutputStream();
zip(bos, fileNames, contents);
return bos.toByteArray();
}catch (Exception e){
e.printStackTrace();
}finally {
IoUtil.close(bos);
}
return null;
}
public static ZipOutputStream getZipOutputStream(OutputStream out, Charset charset) {
return out instanceof ZipOutputStream ? (ZipOutputStream)out : new ZipOutputStream(out, ObjectUtil.defaultIfNull(charset, DEFAULT_CHARSET));
}
public static void main(String[] args) {
String filePath ="D:\\opt\\nginx-1.24.0\\html\\upload\\zip\\";
List<String> files = new ArrayList<>();
files.add("15af21e188d0464b9b5aac128d907208.png");
files.add("83f39c83bcd341f2b5127fb80598fd5c.png");
files.add("86cb00db6a82406e88dca77097e6814e.png");
files.add("307e91bba89345ddb9547e4b27959a02.png");
List<InputStream> iss = new ArrayList<>();
OutputStream os = null;
try {
for(String file : files){
iss.add(Files.newInputStream(Paths.get(filePath + file)));
}
os = new FileOutputStream("D:\\opt\\nginx-1.24.0\\html\\upload\\zip\\zip.zip");
byte[] zip = zip(files, iss);
os.write(zip);
}catch (Exception e){
e.printStackTrace();
}finally {
IoUtil.close(os);
}
}
}
downloadObject
private void downloadObject(List<String> objectNames, OutputStream outputStream) {
if (ObjectUtils.isNotEmpty(objectNames)) {
List<InputStream> iss = null;
try {
iss = new ArrayList<>();
InputStream inputStream;
for (String objectName : objectNames) {
String word = System.getProperty("java.io.tmpdir") + "/" + objectName;
inputStream = new FileInputStream(word);
if (inputStream != null) {
iss.add(inputStream);
}
}
if (ObjectUtils.isNotEmpty(iss)) {
ZipUtils.zip(outputStream, objectNames, iss);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if(iss != null) {
for (InputStream is : iss) {
IOUtils.close(is);
}
}
}
}
}