SpringBoot根据多阶层创建文件,然后压缩成压缩包进行下载
临时接到一个需求说让根据按照下面的这个图片的结构来打包下载指定位置下的文件到指定位置!
实现思路:
1.把已经实现的树形结构的代码进行调用,拿到他的数据进行创建对应的文件夹
2.因为结构下方的文件没有特别直观的数据库中的关联关系,所以还需要对于管理关系进行梳理
3.创建好阶级文件,然后调用网上找的工具类打包成为rar压缩包,然后把路劲交给前端进行调用下载
调用数据,然后传递给创建文件方法进行实现:
/**
* 打包佐证成果文件,压缩成为压缩包!
*
* @param projectId
* @param departId
*/
@ApiOperation(value = "打包佐证成果文件", notes = "佐证与成果-打包佐证成果文件")
@RequestMapping("/exportZip")
public Result<?> exportZip(@RequestParam(name = "projectId", required = false) String projectId, @RequestParam(name = "departId", required = true) String departId) {
// 获取树形结构
Result<List<SelectTreeMoneyModel>> loadAllTreeRoot = this.loadAllTreeRoot(projectId, departId);
// 下载压缩文件,将获取到的树形结构,传递到实现类进行解析跟实现
String downloadZipFile = downloadZipFile(loadAllTreeRoot, "佐证跟成果", "/");
return Result.ok(downloadZipFile);
}
递归的创建子集文件夹,然后调用工具类进行压缩成为压缩包文件,注:删除文件必须捋清楚然后进行使用,其实不删除也只会在指定的位置生成一份,所以我这边没有进行使用!
/**
* 递归的创建子集文件夹
*
* @param data
* @param rootFile
*/
private void mkdirsChild(List<SelectTreeMoneyModel> data, File rootFile) {
for (SelectTreeMoneyModel datum : data) {
// 创建一个file实例对象,指向文件路径(存放照片的根目录)
File childs = new File(rootFile, datum.getTitle());
if (!childs.exists()) {
childs.mkdirs();
}
// 判断如果下面还有子节点,如果有则调用自身
if (!datum.isLeaf()) {
mkdirsChild(datum.getChildren(), childs);
}
// 如果下面没有子节点,则进行判断下面是否有附件,如果有附件则进行下载到指定的文件夹内。
List<ProjectResult> results = iProjectResultService.list(new LambdaQueryWrapper<ProjectResult>().eq(ProjectResult::getTypeId, datum.getKey()));
if (ObjectUtils.isNotEmpty(results)) {
for (ProjectResult result : results) {
List<ProjectTaskContent> projectTaskContents = projectTaskContentService.list(new LambdaQueryWrapper<ProjectTaskContent>().eq(ProjectTaskContent::getResultId, result.getId()));
for (ProjectTaskContent projectTaskContent : projectTaskContents) {
// 判断附件表不是空的,则进行下载文件到对应的文件夹下
if (ObjectUtils.isNotEmpty(projectTaskContents)) {
RestTemplate restTemplate = new RestTemplate();
// 配置文件进行读取
try {
ResponseEntity responseEntity = restTemplate.exchange(url + projectTaskContent.getFilePath(), HttpMethod.GET, null, byte[].class);
byte[] fileContent = (byte[]) responseEntity.getBody();
// 利用 File 对象,然后使用 getName() 方法获取文件名(不包括路径)。
File file = new File(projectTaskContent.getName());
String filenameWithoutPrefix = file.getName();
Files.write(Paths.get(childs + "\\" + filenameWithoutPrefix), fileContent);
} catch (IOException e) {
e.getMessage();
throw new RuntimeException(e);
}
}
}
}
}
}
}
/**
* 下载压缩文件
*
* @param data 数据集合【key:分类名称,value:照片信息集合(key:照片名称,value:照片下载路径)】
* @param fileStr 照片存放的文件路径
* @param zipFileStr 压缩文件的路径(加后缀名)
*/
public String downloadZipFile(Result<List<SelectTreeMoneyModel>> data, String fileStr, String zipFileStr) {
File rootFile = null;
String folderPath = null;
try {
// 遍历传递进来的数据,然后根据传入的数据进行创建文件夹
for (SelectTreeMoneyModel selectTreeMoneyModel : data.getResult()) {
// 创建一个file实例对象,指向文件路径(存放照片的根目录)
zipFileStr = folderUri + selectTreeMoneyModel.getTitle();
folderPath = selectTreeMoneyModel.getTitle();
rootFile = new File(zipFileUri + selectTreeMoneyModel.getTitle());
if (!rootFile.exists()) {
// 创建新文件夹,可以多层(mkdir()创建新文件夹,只能创建一层)
rootFile.mkdirs();
}
// 根据判断递归的创建文件夹,如果是false则有子集
if (!selectTreeMoneyModel.isLeaf()) {
mkdirsChild(selectTreeMoneyModel.getChildren(), rootFile);
}
}
// 创建文件输出流(zip流对象)【实际创建了zip文件,0kb】
FileOutputStream fos1 = new FileOutputStream(new File(zipFileStr + ".zip"));
// 压缩法
toZip1(rootFile, fos1, true);
//TODO 删除文件和压缩文件,要保证每次压缩只保存一份最新的存在。 因为是删除文件,所以要慎用
//delFolder(folderUri + folderPath);
//delFolder(zipFileStr);
} catch (IOException e) {
e.printStackTrace();
}
// 拼接返回的压缩包地址
String urlResult = url + folderPath + ".zip";
return urlResult;
}
/**
* 删除文件夹
*
* @param folderPath 文件夹完整绝对路径
*/
public static void delFolder(String folderPath) {
try {
// 删除目录下所有内容
delAllFile(folderPath);
File myFilePath = new File(folderPath);
//删除空文件夹
myFilePath.delete();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 删除指定文件夹下所有文件
*
* @param path 文件夹完整绝对路径
*/
public static boolean delAllFile(String path) {
boolean bea = false;
File file = new File(path);
if (!file.exists()) {
return bea;
}
if (!file.isDirectory()) {
return bea;
}
//
String[] tempList = file.list();
File temp;
if (tempList != null) {
for (String var : tempList) {
// separator 代替文件或文件夹路径的斜线或反斜线,防止跨平台出现错误
if (path.endsWith(File.separator)) {
temp = new File(path + var);
} else {
temp = new File(path + File.separator + var);
}
if (temp.isFile()) {
temp.delete();
}
if (temp.isDirectory()) {
//先删除文件夹里面的文件
delAllFile(path + "/" + var);
//再删除空文件夹
delFolder(path + "/" + var);
bea = true;
}
}
}
return bea;
}
/**
* 压缩的递归方法
*
* @param sourceFile 源文件
* @param zos zip输出流
* @param fileName 源文件的名称
* @param keepDirStructure 是否保留原来的目录结构,true:保留目录结构;
* false:所有文件跑到压缩包根目录下(注意:不保留目录结构可能会出现同名文件,会压缩失败)
*/
private void compress(File sourceFile, ZipOutputStream zos, String fileName, boolean keepDirStructure) throws IOException {
byte[] buf = new byte[2 * 1024];
// 判断是否是一个文件
if (sourceFile.isFile()) {
// 向zip输出流中添加一个zip实体,构造器中name为zip实体的文件的名字
zos.putNextEntry(new ZipEntry(fileName));
// 创建文件(即某张图片)的输入流
try (FileInputStream in = new FileInputStream(sourceFile)) {
int len;
// read方法:每调用一次就从FileInputStream流中读取一个字节,并返回下一个数据字节,若已到达末尾,就返回-1。
while ((len = in.read(buf, 0, buf.length)) != -1) {
zos.write(buf, 0, len);
}
// 实际写入到了zip输出流的zip实体中,还没写到文件中【zip文件时0kb,不能打开,因为流没有关闭】
zos.closeEntry();
} catch (IOException e) {
throw new IOException(e);
}
} else {
// 源文件时目录
// 获取该目录下所有文件和目录的绝对路径
File[] listFiles = sourceFile.listFiles();
// 空目录
if (listFiles == null || listFiles.length == 0) {
// 需要保留原来的文件结构时,需要对空文件夹进行处理
if (keepDirStructure) {
// 空文件夹的处理
zos.putNextEntry(new ZipEntry(fileName + "/"));
// 没有文件,不需要文件的copy
zos.closeEntry();
}
} else {
// 非空目录
for (File file : listFiles) {
if (keepDirStructure) {
// 注意:getName()仅得到最后一层的名字,不是路径,所以要加“/”,不然所有文件都跑到压缩包根目录下了
compress(file, zos, fileName + "/" + file.getName(), true);
} else {
compress(file, zos, file.getName(), false);
}
}
}
}
}
/**
* 压缩成ZIP 方法1:保留多级目录结构
*
* @param sourceFile 照片存放路径
* @param out 压缩文件输出流
* @param keepDirStructure 是否保留原来的目录结构,true:保留目录结构;
* false:所有文件跑到压缩包根目录下(注意:不保留目录结构可能会出现同名文件,会压缩失败)
*/
public void toZip1(File sourceFile, OutputStream out, boolean keepDirStructure) throws IOException {
long start = System.currentTimeMillis();
// 创建压缩输出流,java7的新语法:Try-with-resources,会确保异常抛出或者try代码块结束时close流【该流必须实现AutoCloseable类】(若是java7之前的版本,则不会生效)
try (ZipOutputStream zos = new ZipOutputStream(out)) {
// 压缩
compress(sourceFile, zos, sourceFile.getName(), keepDirStructure);
long end = System.currentTimeMillis();
System.out.println("压缩完成,耗时:" + (end - start) + " ms");
} catch (IOException e) {
throw new IOException(e);
}
}
最后的实现结果
总结: 主要还是要理清楚你的层级关系,
1.然后在递归的时候一定要递归到最底层,然后根据最底层的数据查找跟附件表有关系的ID进行查找,然后将有关系的文件下载到指定的文件夹下,
2.然后打包成为压缩包这种实现直接可以进行百度查找到适合自己的工具类,如果不是直接适用,可以进行修改工具类的方法进行适配。