前言

文件压缩和下载在后端开发中属于比较常见的功能,一些涉及到证书文件等资源信息,都会有这方面的处理。

本篇博客重点讲述单文件打包压缩文件夹打包压缩压缩文件下载功能的实现。

测试前的准备

开发环境

本次使用到的主要框架、版本如下所示:

  • Springboot 2.1.4.RELEASE
  • org.apache.ant 1.10.5

打包文件如下

ant打jar包 java ant打包springboot_java

注意这里的路径。
如果你的程序文件和待打包文件在同磁盘下,可以将待打包文件夹地址写为/test/zipTest

工具类的配置

既然涉及到单文件文件夹多文件的打包操作,首先需要定义一个实现的工具类 ZipUtils.java

package cn.xj.uitl;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * 文件打包工具类
 */
public class ZipUtils {

    /**
     * 压缩前的文件校验和压缩后压缩文件的构件
     * @param path   要压缩的文件路径
     * @param format 生成的格式(zip、rar)d
     */
    public static void generateFile(String path, String format) throws Exception {

        File file = new File(path);
        // 压缩文件的路径不存在
        if (!file.exists()) {
            throw new Exception("路径 " + path + " 不存在文件,无法进行压缩...");
        }
        // 用于存放压缩文件的文件夹
        File compress = new File(file.getParent());
        // 如果文件夹不存在,进行创建
        if( !compress.exists() ){
            compress.mkdirs();
        }

        // 目的压缩文件
        String absolutePath = compress.getAbsolutePath();
        // 定义压缩操作后,压缩文件的名称,本次采取  文件名XXX.XXX 的形式进行定义(可以自定义)
        String generateFileName = absolutePath + File.separator + file.getName() +format + "." + format;

        // 压缩文件数据的输出流:用于将压缩文件的数据存储至指定的压缩文件中
        FileOutputStream outputStream = new FileOutputStream(generateFileName);

        // 压缩输出流
        ZipOutputStream zipOutputStream = new ZipOutputStream(new BufferedOutputStream(outputStream));

        zipFile(zipOutputStream,file,"");

        System.out.println("源文件位置:" + file.getAbsolutePath() + ",目的压缩文件生成位置:" + generateFileName);
        // 关闭 输出流
        zipOutputStream.close();
    }

    /**
     * 主要的打包压缩操作
     * @param out  输出流
     * @param file 目标文件
     * @param dir  文件夹
     * @throws Exception
     */
    private static void zipFile(ZipOutputStream out, File file, String dir) throws Exception {

        // 如果是文件夹,则采取递归方式继续检索,获取到最终的文件为止,当然这里有性能问题,文件足够大会性能降低
        if (file.isDirectory()) {
            //得到文件列表信息
            File[] files = file.listFiles();

            // 将文件夹添加到下一级打包目录
            // 这里的打包目录必须是每个新的目录(或文件)都需要额外创建新的 ZipEntry 对象
            out.putNextEntry(new ZipEntry(dir + File.separator));

            dir = dir.length() == 0 ? "" : dir + File.separator;

            // 文件夹,则采取递归继续检索,直到识别是文件为止
            for (int i = 0; i < files.length; i++) {
                zipFile(out, files[i], dir + files[i].getName());
            }

            return;
        }

        // 当识别到的 File 对象 是文件时,执行下列逻辑
        // 将文件信息,以流的形式读取到内存中
        FileInputStream inputStream = new FileInputStream(file);
        // 每个新的文件,都需要额外创建一个新的  ZipEntry
        // 将需要打包的文件,放置于新的条目中
        out.putNextEntry(new ZipEntry(dir));

        // 将文件流中的数据信息,分段读取并写入至对应的输出流中
        int len = 0;
        byte[] bytes = new byte[1024];
        while ((len = inputStream.read(bytes)) > 0) {
            out.write(bytes, 0, len);
        }
        // 关闭单个流程资源
        out.closeEntry();

        // 关闭输入流
        inputStream.close();
    }

    public static void main(String[] args) {
        String path = "/test/ziptest";
        String format = "zip";

        try {
            generateFile(path, format);
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println(e.getMessage());
        }
    }
}

先进行本地运行测试

ant打jar包 java ant打包springboot_spring boot_02

服务端的打包文件下载

上面的文件文件夹已经能够进行打包压缩操作,此时则需要使用文件下载操作,将打包好的文件从服务端下载至本地中。

package cn.xj.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.net.URLDecoder;
import java.net.URLEncoder;

@RestController
public class DownloadZip {

    @GetMapping("down")
    public void download(HttpServletResponse response) throws Exception {
        // 首先获取到文件所在的目录
        String filePath = "/test/ziptestzip.zip";

        // 如果是文件夹,则抛出异常
        File file = new File(filePath);
        if(file.isDirectory()){
            throw new RuntimeException("这是文件夹,暂不支持下载!");
        }

        // 判断文件是否存在
        if(!file.exists()){
            throw new RuntimeException("文件不存在!");
        }

        // 读取文件,将磁盘中的文件加载至内存
        FileInputStream inputStream = new FileInputStream(file);

        // 这里只是为了防止出错,将 response 重置
        response.reset();
        // 防止乱码
        response.setCharacterEncoding("utf-8");
        // 设置相关格式
        response.setContentType("application/force-download");
        String name = "香蕉下载文件.zip";
        // 解决下载文件名乱码的问题
        String disposition  = "attachment;filename*=utf-8'zh_cn'" + URLEncoder.encode(name, "utf-8");
        response.setHeader("Content-Disposition", disposition);
        // 将输出流信息发送至客户端
        ServletOutputStream out = response.getOutputStream();

        // 读取并发送
        int len = 0;
        byte[] bytes = new byte[1024];
        while ((len = inputStream.read(bytes)) > 0){
            out.write(bytes,0,len);
        }

        inputStream.close();
        out.close();
    }
}

运行程序,并测试下载:

http://localhost/down

ant打jar包 java ant打包springboot_ant打jar包 java_03


代码下载

gitee代码仓库地址