Java实现多个文件生成压缩包下载
目录
1、工具类代码
package com.shucha.digitalportal.biz.utils;
import com.shucha.digitalportal.biz.model.FileInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipOutputStream;
import java.io.*;
import java.util.List;
import static org.springframework.util.StreamUtils.BUFFER_SIZE;
/**
* 附件下载 生成压缩包
*/
@Slf4j
public class ZipUtils {
/**
* 多个文件压缩下载
* @param outputStream
* @param fileList 多个file文件list
* @throws IOException
*/
public static void downloadZip(OutputStream outputStream, List<FileInfo> fileList) throws IOException {
try (ZipOutputStream zos = new ZipOutputStream(outputStream)) {
zos.setEncoding("GBK");
for (FileInfo fileInfo : fileList) {
File file = new File(fileInfo.getFilePath());
if (file.exists()) {
ZipEntry zipEntry = new ZipEntry(fileInfo.getFileName());
zos.putNextEntry(zipEntry);
byte[] buf = new byte[BUFFER_SIZE];
int len;
FileInputStream in = new FileInputStream(file);
while ((len = in.read(buf)) != -1) {
zos.write(buf, 0, len);
zos.flush();
}
}
}
zos.flush();
} catch (IOException e) {
log.error("文件压缩异常:", e);
throw e;
} finally {
try {
if (outputStream != null) {
outputStream.close();
}
} catch (IOException e) {
log.error("导出文件关闭流异常", e);
}
}
}
/**
* 压缩的文件就是传递过来的文件名称
* @param outputStream
* @param fileList
* @throws IOException
*/
public static void downloadZipOldName(OutputStream outputStream, List<File> fileList) throws IOException {
try (ZipOutputStream zos = new ZipOutputStream(outputStream)) {
zos.setEncoding("GBK");
for (File file : fileList) {
if (file.exists()) {
ZipEntry zipEntry = new ZipEntry(file.getName());
zos.putNextEntry(zipEntry);
byte[] buf = new byte[BUFFER_SIZE];
int len;
FileInputStream in = new FileInputStream(file);
while ((len = in.read(buf)) != -1) {
zos.write(buf, 0, len);
zos.flush();
}
}
}
zos.flush();
} catch (IOException e) {
log.error("文件压缩异常:", e);
throw e;
} finally {
try {
if (outputStream != null) {
outputStream.close();
}
} catch (IOException e) {
log.error("导出文件关闭流异常", e);
}
}
}
/**
* 将文件夹及文件夹包含的内容压缩成zip文件
* (为了解决中文乱码的问题,ZipOutputStream用org.apache.tools.zip.*)
*
* @param inputFile 源文件
* @param delFlag 删除源文件标记
* @return File 压缩后的文件
*/
public static File zipCompress(File inputFile, boolean delFlag) throws Exception {
File zipFile = null;
//创建zip输出流
//为了解决中文乱码的问题,ZipOutputStream用org.apache.tools.zip.*
//不要用 java.util.zip.*
if (inputFile != null && inputFile.exists()) {
try {
String path = inputFile.getCanonicalPath();
String zipFileName = path + ".zip";
zipFile = new File(zipFileName);
if (zipFile.exists()) {
zipFile.delete();
}
zipFile.createNewFile();//创建文件
try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFile))){
//解决中文乱码问题,指定编码GBK
zos.setEncoding("GBK");
//压缩文件或文件夹
compressFile(zos, inputFile, inputFile.getName());
}
} catch (Exception e) {
log.error("文件压缩异常:", e);
throw e;
} finally {
if (delFlag) {
//递归删除源文件及源文件夹
deleteFile(inputFile);
}
}
}
return zipFile;
}
/**
* 压缩文件或文件夹
* (ZipEntry 使用org.apache.tools.zip.*,不要用 java.util.zip.*)
*
* @param zos zip输出流
* @param sourceFile 源文件
* @param baseName 父路径
* @throws Exception 异常
*/
private static void compressFile(ZipOutputStream zos, File sourceFile, String baseName) throws Exception {
if (!sourceFile.exists()) {
return;
}
//若路径为目录(文件夹)
if (sourceFile.isDirectory()) {
//取出文件夹中的文件(或子文件夹)
File[] fileList = sourceFile.listFiles();
//若文件夹为空,则创建一个目录进入点
if (fileList.length == 0) {
//文件名称后跟File.separator表示这是一个文件夹
zos.putNextEntry(new ZipEntry(baseName + File.separator));
//若文件夹非空,则递归调用compressFile,对文件夹中的每个文件或每个文件夹进行压缩
} else {
for (int i = 0; i < fileList.length; i++) {
compressFile(zos, fileList[i],
baseName + File.separator + fileList[i].getName());
}
}
//若为文件,则先创建目录进入点,再将文件写入zip文件中
} else {
ZipEntry ze = new ZipEntry(baseName);
//设置ZipEntry的最后修改时间为源文件的最后修改时间
ze.setTime(sourceFile.lastModified());
zos.putNextEntry(ze);
try (FileInputStream fis = new FileInputStream(sourceFile)) {
copyStream(fis, zos);
}
}
}
/**
* 流拷贝
*
* @param in 输入流
* @param out 输出流
* @throws IOException
*/
private static void copyStream(InputStream in, OutputStream out) throws IOException {
int bufferLength = 1024 * 100;
synchronized (in) {
synchronized (out) {
int count = 0;
byte[] buffer = new byte[bufferLength];
while ((count = in.read(buffer, 0, bufferLength)) != -1) {
out.write(buffer, 0, count);
}
out.flush();
}
}
}
/**
* 递归删除文件夹中的目录及文件
*
* @param sourceFile
* @throws Exception
*/
private static void deleteFile(File sourceFile) throws Exception {
//如果路径为目录
if (sourceFile.isDirectory()) {
//取出文件夹中的文件或子文件夹
File[] fList = sourceFile.listFiles();
if (fList.length == 0) {
sourceFile.delete();
} else {
for (int i = 0; i < fList.length; i++) {
deleteFile(fList[i]);
}
sourceFile.delete();
}
//如果为文件则直接删除
} else {
sourceFile.delete();
}
}
}
2、FileInfo实体类
package com.shucha.digitalportal.biz.model;
import java.util.Date;
import com.baomidou.mybatisplus.annotation.TableId;
import com.sdy.common.model.BaseModel;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.sdy.common.utils.DateUtil;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* <p>
* 附件信息
* </p>
*
* @author tqf
* @since 2021-07-15
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
public class FileInfo extends BaseModel {
private static final long serialVersionUID = 1L;
/**
* 主键
*/
@ApiModelProperty(value = "主键")
@JsonSerialize(using = ToStringSerializer.class)
@TableId
private Long id;
/**
* 数据主键ID
*/
@ApiModelProperty(value = "数据主键ID")
private Long dataId;
/**
* 附件名称
*/
@ApiModelProperty(value = "附件名称")
private String fileName;
/**
* 附件路径
*/
@ApiModelProperty(value = "附件路径")
private String filePath;
/**
* 附件类型(1-通知公告,2-风险气象台)
*/
@ApiModelProperty(value = "附件类型(1-通知公告,2-风险气象台)")
private Integer fileType;
/**
* 创建时间
*/
@ApiModelProperty(value = "创建时间")
@JsonFormat(pattern = DateUtil.DATETIME_FORMAT)
private Date createTime;
/**
* 更新时间
*/
@ApiModelProperty(value = "更新时间")
@JsonFormat(pattern = DateUtil.DATETIME_FORMAT)
private Date updateTime;
}
3、控制层调用代码
@GetMapping(value = "/downFileZip")
@ApiOperation(value = "多个附件,打包成zip压缩包下载")
@ApiImplicitParams({
@ApiImplicitParam(name = "id", value = "数据ID", required = true),
@ApiImplicitParam(name = "fileType", value = "附件类型(1-通知公告)", required = true)
})
public void downFileZip(HttpServletResponse response, Long id, Integer fileType) throws Exception {
Assert.isNull(id,"附件地址ID不能为空");
Assert.isNull(fileType,"附件类型不能为空");
OutputStream out = null;
FileInputStream fis = null;
//文件名
String fileName = null;
// 根据数据ID和附件类型 查询附件数据
List<FileInfo> list = fileInfoService.list(Wrappers.<FileInfo>lambdaQuery()
.eq(FileInfo::getDataId, id)
.eq(FileInfo::getFileType,fileType));
Assert.isTrue(list.size() ==0,"没有对应的附件可下载!");
String oldFileName = list.get(0).getFileName();
int t = oldFileName.lastIndexOf(".");
fileName = t > 0 ? oldFileName.substring(0, t) : oldFileName;
try {
out = response.getOutputStream();
// 附件文件放入文件list
List<File> fileList = new ArrayList<>();
for(int i=0;i<list.size();i++){
File file = new File(list.get(i).getFilePath());
fileList.add(i,file);
}
// 设置response的Header 防止文件名乱码
response.addHeader("Content-Disposition",
"attachment;filename="
+ new String((fileName+".zip").getBytes("utf-8"), "ISO-8859-1"));
response.setContentType("application/force-download");
ZipUtils.downloadZip(out,list);
// 下面的方法压缩的文件名称是上传文件生成的文件名称
// ZipUtils.downloadZipOldName(out,fileList);
out.flush();
} catch (IOException e) {
e.printStackTrace();
log.error("执行 文件下载downFile 方法失败:" + e.getMessage());
} finally {
if (out != null) {
out.close();
}
}
}