java 生产.gz文件并将多个文件压缩进去

目前根据如下工具类的zipFile()方法生产了.gz的压缩包

public static void main(String[] args) {
try {
zipFile("D:/test.xlsx","D:/test.xlsx.zip",);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
我如果想塞多个文件进入压缩包该怎么处理,现有的方法调用多次,并不会让第二个文件也压缩进去,如果要满足该条件需要怎么改写工具类,
注:(不是压缩文件夹,而是选择一个文件夹里个别的文件进行压缩)

package utils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

/**

  • 压缩文件工具类
  • /
    public class GZipUtils {
    /**
    * Member cache 文件解压处理 
    *  
    * @param buf 
    * @return 
    * @throws IOException 
    */  
    
    public static byte[] unGzip(byte[] buf) throws IOException {
    GZIPInputStream gzi = null;  
    ByteArrayOutputStream bos = null;  
    try {  
     gzi = new GZIPInputStream(new ByteArrayInputStream(buf));  
     bos = new ByteArrayOutputStream(buf.length);  
     int count = 0;  
     byte[] tmp = new byte[2048];  
     while ((count = gzi.read(tmp)) != -1) {  
      bos.write(tmp, 0, count);  
     }  
     buf = bos.toByteArray();  
    } finally {  
     if (bos != null) {  
      bos.flush();  
      bos.close();  
     }  
     if (gzi != null)  
      gzi.close();  
    }  
    return buf;  
    
    } /**
    * Member cache 文件压缩处理 
    *  
    * @param val 
    * @return 
    * @throws IOException 
    */  
    
    public static byte[] gzip(byte[] val) throws IOException {
    ByteArrayOutputStream bos = new ByteArrayOutputStream(val.length);  
    GZIPOutputStream gos = null;  
    try {  
     gos = new GZIPOutputStream(bos);  
     gos.write(val, 0, val.length);  
     gos.finish();  
     gos.flush();  
     bos.flush();  
     val = bos.toByteArray();  
    } finally {  
     if (gos != null)  
      gos.close();  
     if (bos != null)  
      bos.close();  
    }  
    return val;  
    
    } /**
    * 对文件进行压缩 
    *  
    * @param source 
    *            源文件 
    * @param target 
    *            目标文件 
    * @throws IOException 
    */  
    
    public static void zipFile(String source, String target) throws IOException {
    FileInputStream fin = null;  
    FileOutputStream fout = null;  
    GZIPOutputStream gzout = null;  
    try {  
     fin = new FileInputStream(source);  
     fout = new FileOutputStream(target);  
     gzout = new GZIPOutputStream(fout);  
     byte[] buf = new byte[1024];  
     int num;  
     while ((num = fin.read(buf)) != -1) {  
      gzout.write(buf, 0, num);  
     }  
    } finally {  
     if (gzout != null)  
      gzout.close();  
     if (fout != null)  
      fout.close();  
     if (fin != null)  
      fin.close();  
    }  
    
    } /**
    * 解压文件 
    *  
    * @param source源文件 
    * @param target目标文件 
    * @throws IOException 
    */  
    
    public static void unZipFile(String source, String target)
     throws IOException {  
    FileInputStream fin = null;  
    GZIPInputStream gzin = null;  
    FileOutputStream fout = null;  
    try {  
     fin = new FileInputStream(source);  
     gzin = new GZIPInputStream(fin);  
     fout = new FileOutputStream(target);  
     byte[] buf = new byte[1024];  
     int num;  
     while ((num = gzin.read(buf, 0, buf.length)) != -1) {  
      fout.write(buf, 0, num);  
     }  
    } finally {  
     if (fout != null)  
      fout.close();  
     if (gzin != null)  
      gzin.close();  
     if (fin != null)  
      fin.close();  
    }  
    
    } /*
    * 字节数组解压缩后返回字符串 
    */  
    
    public static String uncompressToString(byte[] b) {
       if (b == null || b.length == 0) {  
           return null;  
       }  
       ByteArrayOutputStream out = new ByteArrayOutputStream();  
       ByteArrayInputStream in = new ByteArrayInputStream(b);  
    
       try {  
           GZIPInputStream gunzip = new GZIPInputStream(in);  
           byte[] buffer = new byte[256];  
           int n;  
           while ((n = gunzip.read(buffer)) >= 0) {  
               out.write(buffer, 0, n);  
           }  
       } catch (IOException e) {  
           e.printStackTrace();  
       }  
       return out.toString();  
    
    } public static void main(String[] args) {
       try {
          unZipFile("D:/test.xlsx.zip", "D:/test.xlsx");
      } catch (IOException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
      } 
    
    }
    }

你的方法不行是因为调用多次只会覆盖之前写入的而保留最后一次调用写入的。
需要将多个source传入压缩方法中,进行循环读取。

public static void zipFile(List<String> fileUrl, String target) throws IOException {
FileInputStream fin = null;
FileOutputStream fout = null;
GZIPOutputStream gzout = null;
try {
fout = new FileOutputStream(target);
gzout = new GZIPOutputStream(fout);
for (int i = 0; i < fileUrl.size(); i++) {
    fin = new FileInputStream(fileUrl.get(i));
    byte[] buf = new byte[1024];
    int num;
    while ((num = fin.read(buf)) != -1) {
        gzout.write(buf, 0, num);
    }
    fin.close();
}

} finally {
if (gzout != null)
gzout.close();
if (fout != null)
fout.close();
if (fin != null)
fin.close();
}
}

看一下。

压缩文件
  在Java中,可以 使用GZIPOutputStream创建gzip(gz)压缩文件,它在commons-compress下面,可以通过如下的maven坐标引入:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-compress</artifactId>
    <version>1.21</version>
</dependency>
tar.gz文件可以理解为通过如下方式获取的文件:先用tar打包,再使用gz进行压缩。下面直接上代码:

package com.eg.wiener.utils;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.compress.utils.IOUtils;
import org.apache.commons.lang3.RandomStringUtils;

import java.io.*;
import java.util.zip.GZIPOutputStream;

@Slf4j
public class FileUtils {

    /**
     *
     * 压缩文件
     *
     * @param sourceFolder 指定打包的源目录
     * @param tarGzPath    指定目标 tar 包的位置
     */
    private static void compress(String sourceFolder, String tarGzPath) {
        log.info("压缩后文件名:{}", tarGzPath);
        TarArchiveOutputStream tarOs = null;
        try {
            // 创建一个 FileOutputStream 到输出文件(.tar.gz)
            FileOutputStream fos = new FileOutputStream(tarGzPath);
            // 创建一个 GZIPOutputStream,用来包装 FileOutputStream 对象
            GZIPOutputStream gos = new GZIPOutputStream(new BufferedOutputStream(fos));
            // 创建一个 TarArchiveOutputStream,用来包装 GZIPOutputStream 对象
            tarOs = new TarArchiveOutputStream(gos);
            // 使文件名支持超过 100 个字节
            tarOs.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX);
            File sourceFile = new File(sourceFolder);
            //遍历源目录的文件,将所有文件迁移到新的目录tarGzPath下
            File[] sources = sourceFile.listFiles();
            for (File oneFile : sources) {
                addFilesToTarGZ(oneFile.getPath(), "", tarOs);
            }
        } catch (IOException e) {
            log.error("压缩失败,", e);
        } finally {
            try {
                tarOs.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * @param sourcePath 源文件
     * @param parent     源目录
     * @param tarArchive 压缩输出流
     * @throws IOException
     */
    public static void addFilesToTarGZ(String sourcePath, String parent, TarArchiveOutputStream tarArchive) throws IOException {
        File sourceFile = new File(sourcePath);
        // 获取新目录下的文件名称
        String fileName = parent.concat(sourceFile.getName());
        //打包压缩该文件
        tarArchive.putArchiveEntry(new TarArchiveEntry(sourceFile, fileName));
        if (sourceFile.isFile()) {
            FileInputStream fis = new FileInputStream(sourceFile);
            BufferedInputStream bis = new BufferedInputStream(fis);
            // 写入文件
            IOUtils.copy(bis, tarArchive);
            tarArchive.closeArchiveEntry();
            bis.close();
        } else if (sourceFile.isDirectory()) {
            // 因为是个文件夹,无需写入内容,关闭即可
            tarArchive.closeArchiveEntry();
            // 遍历文件夹下的文件
            for (File f : sourceFile.listFiles()) {
                // 递归遍历文件目录树
                addFilesToTarGZ(f.getAbsolutePath(), fileName + File.separator, tarArchive);
            }
        }
    }

}
解压文件
  解压文件时依赖的jar包如下:

       <!--解压-->
        <dependency>
            <groupId>org.apache.ant</groupId>
            <artifactId>ant</artifactId>
            <version>1.10.5</version>
        </dependency>
  压缩文件时遇到了繁琐的关闭资源的问题,故这里优先考虑使用try-with-resources,而不是try-finally。由此带来的好处是使代码更简洁、更清晰,即便是抛出的异常,也更有价值,这些优点是try-finally无法做到的。

   /**
     * 解压tar.gz 文件
     * @param targzFile 要解压的tar.gz文件对象
     * @param outPath 要解压到某个指定的目录下
     * @throws IOException
     */
    public static void unpack(File targzFile, String outPath) {

        // 验证参数
        if (targzFile == null || !targzFile.isFile() || StringUtils.isEmpty(outPath)) {
            log.error("文件解压缩执行异常,请检查输入参数!");
            return;
        }
        // 读取 .tar.gz 文件转换为 tar 文件
        try (FileInputStream is = new FileInputStream(targzFile);
             BufferedInputStream bis = new BufferedInputStream(is);
             GZIPInputStream gzipIs = new GZIPInputStream(bis);
             TarInputStream tarIs = new TarInputStream(gzipIs, 1024 * 2)) {
            // 迭代 tar 文件集合,解压文件
            for (TarEntry entry = tarIs.getNextEntry(); entry != null; entry = tarIs.getNextEntry()) {
                File targetFileName = new File(outPath + "/" + entry.getName());
                IOUtils.copy(tarIs, new FileOutputStream(targetFileName));
            }
            log.info("文件 {} 解压完毕", targzFile);
        } catch (Exception e) {
            log.error("{} 解压异常!", targzFile, e);
        }
    }
  测试用例就放在一个main函数里了,如下所示:

    public static void main(String[] args) throws IOException {
        // 把F:\img\source内的文件及其文件夹打包成名为文件夹F:\img\target下的、名字为六位随机数的 gz 压缩包
        String targetPath = "F:\\img\\target\\" + RandomStringUtils.randomAlphanumeric(6) + ".tar.gz";
        compress("F:\\img\\source", targetPath);
        log.info("=====done====");
        unpack(new File(targetPath) , "F:\\img\\unpack");
    }

个人将所有要压缩的文件(允许在不同的文件夹下)放到一个临时文件夹中,然后直接压缩临时文件夹更方便,而且不用来回循环那么麻烦,只用写一次压缩流即可

new FileOutputStream(target,true);
用这种append的方式,每次调用就是追加的。不然你每次调用都会覆盖掉之前的数据。

java追加文件到ZIP

import java.io.File;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;

import java.io.OutputStream;import java.util.Enumeration;import java.util.zip.ZipEntry;

import java.util.zip.ZipFile;import java.util.zip.ZipOutputStream;public class Main {

// 4MB buffer

private static final byte[] BUFFER = new byte[4096 * 1024];

/**

* copy input to output stream - available in several StreamUtils or Streams classes

*/

public static void copy(InputStream input, OutputStream output) throws IOException {

int bytesRead;

while ((bytesRead = input.read(BUFFER))!= -1) {

output.write(BUFFER, 0, bytesRead);

}

}

public static void main(String[] args) throws Exception {

// read war.zip and write to append.zip

ZipFile war = new ZipFile("war.zip");

ZipOutputStream append = new ZipOutputStream(new FileOutputStream("append.zip"));

// first, copy contents from existing war

Enumeration extends ZipEntry> entries = war.entries();

while (entries.hasMoreElements()) {

ZipEntry e = entries.nextElement();

System.out.println("copy: " + e.getName());

append.putNextEntry(e);

if (!e.isDirectory()) {

copy(war.getInputStream(e), append);

}

append.closeEntry();

}

// now append some extra content

ZipEntry e = new ZipEntry("answer.txt");

System.out.println("append: " + e.getName());

append.putNextEntry(e);

append.write("42\n".getBytes());

append.closeEntry();

// close

war.close();

append.close();

}}


可以参考这篇文章 https://zhhll.icu/2022/java%E5%9F%BA%E7%A1%80/IO/3.zip%E5%8E%8B%E7%BC%A9/