Java获取zip文件流

项目中经常遇到需要导出压缩文件的情况,需要压缩的数据来源有网络数据、静态资源,常见的导出场景有:操作日志、密钥文件的导出等。

导出方案
  1. 生成zip文件保存到磁盘,返回时读取文件内容

需要考虑文件的生成策略,防止并发情况下文件冲突;文件的删除策略,无用文件及时删除,避免磁盘空间的浪费。

  1. 保存到内存中,返回时从内存中读取二进制内容

以流的形式传输数据,避免临时文件的生成,完成后关闭流,zip文件过大时,会明显增加内容使用量。

方案1示例代码
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @author: dkangel
 * @since: 2019/11/23 10:51
 * @Description:
 */
@RestController
public class ZipDownloadController {

    private static final Logger LOGGER = LoggerFactory.getLogger(ZipDownloadController.class);
    private static final String ZIP_CONTENT_TYPE = "application/zip";

    /**
     * 生成临时文件,使用后删除
     *
     * @param response
     */
    @RequestMapping(value = "/download", method = RequestMethod.POST)
    public void downloadZipFile(HttpServletResponse response) {
        // 创建临时文件
        File zipFile = new File("test.zip");
        try (ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(zipFile))) {
            // 写入数据
            zipOutputStream.putNextEntry(new ZipEntry("test.txt"));
            String fileContent = "Hello world.";
            zipOutputStream.write(fileContent.getBytes());

            zipOutputStream.closeEntry();
            zipOutputStream.flush();
            zipOutputStream.finish();
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 将zip文件数据读入输入流中,并删除文件
        String fileName = zipFile.getName();
        InputStream inputStream = null;
        try {
            inputStream = new FileInputStream(zipFile);
            // 删除临时文件,需要考虑异常情况下如何保证文件被删除
            if (zipFile.delete()) {
                LOGGER.info("File:{} has been removed.", fileName);
            }
        } catch (FileNotFoundException e) {
            LOGGER.error("File does not exist.");
            e.printStackTrace();
        }
        if (inputStream == null) {
            LOGGER.error("inputStream is null.");
            throw new RuntimeException();
        }

        // 将输入流数据复制到输出流,并关闭流
        response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
        response.setCharacterEncoding("utf-8");
        response.setContentType(ZIP_CONTENT_TYPE);
        try {
            // 使用工具类代替手动逐行copy
            IOUtils.copy(inputStream, response.getOutputStream());
            response.flushBuffer();
        } catch (Exception ex) {
            LOGGER.error(ex.getLocalizedMessage());
        } finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
方案2示例代码
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @author: dkangel
 * @since: 2019/11/23 10:51
 * @Description:
 */
@RestController
public class ZipDownloadController {

    private static final String ZIP_CONTENT_TYPE = "application/zip";

    /**
     * 文件内容存到内存中,不生成临时文件
     *
     * @return
     */
    @RequestMapping(value = "/download", method = RequestMethod.POST)
    public ResponseEntity downloadZipFile() {
        // 创建字节数组缓冲区,所有发送到输出流的数据保存在该字节数组缓冲区中
        ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream();
        try (ZipOutputStream zipOutputStream = new ZipOutputStream(arrayOutputStream)) {
            // 写入数据
            zipOutputStream.putNextEntry(new ZipEntry("test.txt"));
            String fileContent = "Hello world.";
            zipOutputStream.write(fileContent.getBytes());

            zipOutputStream.closeEntry();
            zipOutputStream.flush();
            zipOutputStream.finish();
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 定义返回头,设置文件名
        HttpHeaders httpHeaders = new HttpHeaders();
        String zipFileName = "test.zip";
        httpHeaders.add("Content-disposition", "attachment; filename=" + zipFileName);
        try {
            return ResponseEntity.ok()
                    .headers(httpHeaders)
                    .contentType(MediaType.parseMediaType(ZIP_CONTENT_TYPE))
                    .body(arrayOutputStream.toByteArray());
        } finally {
            try {
                arrayOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

Tips:

  1. 多文件情况下,可以将文件内容存入List中(包含文件名/文件内容),之后逐个写入ZipOutStream中。
  2. 本地生成Zip文件请看 Java 文件/文件夹压缩.

你可能感兴趣的:(java,Java,zip,压缩,文件流)