Java 如何实现解压缩文件和文件夹

一 前言

项目开发中,总会遇到解压缩文件的时候。比如,用户下载多个文件时,服务端可以将多个文件压缩成一个文件(例如xx.zip或xx.rar)。用户上传资料时,允许上传压缩文件,服务端进行解压读取每一个文件。

基于通用性,以下介绍几种解压缩文件的方式,包装成工具类,供平时开发使用。

二 压缩文件

压缩文件,顾名思义,即把一个或多个文件压缩成一个文件。压缩也有2种形式,一种是将所有文件压缩到同一目录下,此种方式要注意文件重名覆盖的问题。另一种是按原有文件树结构进行压缩,即压缩后的文件树结构保持不变。

压缩文件操作,会使用到一个类,即ZipOutputStream。

2.1 压缩多个文件

此方法将所有文件压缩到同一个目录下。方法传入多个文件列表,和一个最终压缩到的文件路径名。

  /**
   * 压缩多个文件,压缩后的所有文件在同一目录下
   *
   * @param zipFileName 压缩后的文件名
   * @param files 需要压缩的文件列表
   * @throws IOException IO异常
   */
  public static void zipMultipleFiles(String zipFileName, File... files) throws IOException {
    ZipOutputStream zipOutputStream = null;
    try {
      // 输出流
      zipOutputStream = new ZipOutputStream(new FileOutputStream(zipFileName));
      // 遍历每一个文件,进行输出
      for (File file : files) {
        zipOutputStream.putNextEntry(new ZipEntry(file.getName()));
        FileInputStream fileInputStream = new FileInputStream(file);
        int readLen;
        byte[] buffer = new byte[1024];
        while ((readLen = fileInputStream.read(buffer)) != -1) {
          zipOutputStream.write(buffer, 0, readLen);
        }
        // 关闭流
        fileInputStream.close();
        zipOutputStream.closeEntry();
      }
    } finally {
      if (null != zipOutputStream) {
        try {
          zipOutputStream.close();
        } catch (IOException ex) {
          ex.printStackTrace();
        }
      }
    }
  }

测试,将D盘下的infp.txt和infp1.txt文件压缩到D盘下,压缩文件名为my.zip。

  public static void main(String[] args) throws Exception {
    zipMultipleFiles("D:/my.zip", new File("D:/infp.txt"), new File("D:/infp1.txt"));
  }

2.2 压缩文件或文件树

此方法将文件夹下的所有文件按原有的树形结构压缩到文件中,也支持压缩单个文件。原理也简单,无非就是递归遍历文件树中的每一个文件,进行压缩。有个注意的点每一个文件的写入路径是基于压缩文件位置的相对路径。

package com.nobody.zip;

import java.io.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

public class ZipUtils {

  /**
   * 压缩文件或文件夹(包括所有子目录文件)
   *
   * @param sourceFile 源文件
   * @param format 格式(zip或rar)
   * @throws IOException 异常信息
   */
  public static void zipFileTree(File sourceFile, String format) throws IOException {
    ZipOutputStream zipOutputStream = null;
    try {
      String zipFileName;
      if (sourceFile.isDirectory()) { // 目录
        zipFileName = sourceFile.getParent() + File.separator + sourceFile.getName() + "."
            + format;
      } else { // 单个文件
        zipFileName = sourceFile.getParent()
            + sourceFile.getName().substring(0, sourceFile.getName().lastIndexOf("."))
            + "." + format;
      }
      // 压缩输出流
      zipOutputStream = new ZipOutputStream(new FileOutputStream(zipFileName));
      zip(sourceFile, zipOutputStream, "");
    } finally {
      if (null != zipOutputStream) {
        // 关闭流
        try {
          zipOutputStream.close();
        } catch (IOException ex) {
          ex.printStackTrace();
        }
      }
    }
  }

  /**
   * 递归压缩文件
   *
   * @param file 当前文件
   * @param zipOutputStream 压缩输出流
   * @param relativePath 相对路径
   * @throws IOException IO异常
   */
  private static void zip(File file, ZipOutputStream zipOutputStream, String relativePath)
      throws IOException {

    FileInputStream fileInputStream = null;
    try {
      if (file.isDirectory()) { // 当前为文件夹
        // 当前文件夹下的所有文件
        File[] list = file.listFiles();
        if (null != list) {
          // 计算当前的相对路径
          relativePath += (relativePath.length() == 0 ? "" : "/") + file.getName();
          // 递归压缩每个文件
          for (File f : list) {
            zip(f, zipOutputStream, relativePath);
          }
        }
      } else { // 压缩文件
        // 计算文件的相对路径
        relativePath += (relativePath.length() == 0 ? "" : "/") + file.getName();
        // 写入单个文件
        zipOutputStream.putNextEntry(new ZipEntry(relativePath));
        fileInputStream = new FileInputStream(file);
        int readLen;
        byte[] buffer = new byte[1024];
        while ((readLen = fileInputStream.read(buffer)) != -1) {
          zipOutputStream.write(buffer, 0, readLen);
        }
        zipOutputStream.closeEntry();
      }
    } finally {
      // 关闭流
      if (fileInputStream != null) {
        try {
          fileInputStream.close();
        } catch (IOException ex) {
          ex.printStackTrace();
        }
      }
    }
  }

  public static void main(String[] args) throws Exception {
    String path = "D:/test";
    String format = "zip";
    zipFileTree(new File(path), format);
  }
}

上例将test目录下的所有文件压缩到同一目录下的test.zip文件中。

2.3 借助文件访问器压缩

还有一种更简单的方式,我们不自己写递归遍历。借助Java原生类,SimpleFileVisitor,它提供了几个访问文件的方法,其中有个方法visitFile,对于文件树中的每一个文件(文件夹除外),都会调用这个方法。我们只要写一个类继承SimpleFileVisitor,然后重写visitFile方法,实现将每一个文件写入到压缩文件中即可。

当然,除了visitFile方法,它里面还有preVisitDirectory,postVisitDirectory,visitFileFailed等方法,通过方法名大家也猜出什么意思了。

package com.nobody.zip;

import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @Description
 * @Author Mr.nobody
 * @Date 2021/3/8
 * @Version 1.0.0
 */
public class ZipFileTree extends SimpleFileVisitor<Path> {

  // zip输出流
  private ZipOutputStream zipOutputStream;
  // 源目录
  private Path sourcePath;

  public ZipFileTree() {}

  /**
   * 压缩目录以及所有子目录文件
   *
   * @param sourceDir 源目录
   */
  public void zipFile(String sourceDir) throws IOException {
    try {
      // 压缩后的文件和源目录在同一目录下
      String zipFileName = sourceDir + ".zip";
      this.zipOutputStream = new ZipOutputStream(new FileOutputStream(zipFileName));
      this.sourcePath = Paths.get(sourceDir);

      // 开始遍历文件树
      Files.walkFileTree(sourcePath, this);
    } finally {
      // 关闭流
      if (null != zipOutputStream) {
        zipOutputStream.close();
      }
    }
  }

  // 遍历到的每一个文件都会执行此方法
  @Override
  public FileVisitResult visitFile(Path file, BasicFileAttributes attributes) throws IOException {
    // 取相对路径
    Path targetFile = sourcePath.relativize(file);
    // 写入单个文件
    zipOutputStream.putNextEntry(new ZipEntry(targetFile.toString()));
    byte[] bytes = Files.readAllBytes(file);
    zipOutputStream.write(bytes, 0, bytes.length);
    zipOutputStream.closeEntry();
    // 继续遍历
    return FileVisitResult.CONTINUE;
  }

  // 遍历每一个目录时都会调用的方法
  @Override
  public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
      throws IOException {
    return super.preVisitDirectory(dir, attrs);
  }

  // 遍历完一个目录下的所有文件后,再调用这个目录的方法
  @Override
  public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
    return super.postVisitDirectory(dir, exc);
  }

  // 遍历文件失败后调用的方法
  @Override
  public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
    return super.visitFileFailed(file, exc);
  }

  public static void main(String[] args) throws IOException {
    // 需要压缩源目录
    String sourceDir = "D:/test";
    // 压缩
    new ZipFileTree().zipFile(sourceDir);
  }
}

三 解压文件

解压压缩包,借助ZipInputStream类,可以读取到压缩包中的每一个文件,然后根据读取到的文件属性,写入到相应路径下即可。对于解压压缩包中是文件树的结构,每读取到一个文件后,如果是多层路径下的文件,需要先创建父目录,再写入文件流。

package com.nobody.zip;

import java.io.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

/**
 * @Description 解压缩文件工具类
 * @Author Mr.nobody
 * @Date 2021/3/8
 * @Version 1.0.0
 */
public class ZipUtils {

  /**
   * 解压
   *
   * @param zipFilePath 带解压文件
   * @param desDirectory 解压到的目录
   * @throws Exception
   */
  public static void unzip(String zipFilePath, String desDirectory) throws Exception {

    File desDir = new File(desDirectory);
    if (!desDir.exists()) {
      boolean mkdirSuccess = desDir.mkdir();
      if (!mkdirSuccess) {
        throw new Exception("创建解压目标文件夹失败");
      }
    }
    // 读入流
    ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(zipFilePath));
    // 遍历每一个文件
    ZipEntry zipEntry = zipInputStream.getNextEntry();
    while (zipEntry != null) {
      if (zipEntry.isDirectory()) { // 文件夹
        String unzipFilePath = desDirectory + File.separator + zipEntry.getName();
        // 直接创建
        mkdir(new File(unzipFilePath));
      } else { // 文件
        String unzipFilePath = desDirectory + File.separator + zipEntry.getName();
        File file = new File(unzipFilePath);
        // 创建父目录
        mkdir(file.getParentFile());
        // 写出文件流
        BufferedOutputStream bufferedOutputStream =
            new BufferedOutputStream(new FileOutputStream(unzipFilePath));
        byte[] bytes = new byte[1024];
        int readLen;
        while ((readLen = zipInputStream.read(bytes)) != -1) {
          bufferedOutputStream.write(bytes, 0, readLen);
        }
        bufferedOutputStream.close();
      }
      zipInputStream.closeEntry();
      zipEntry = zipInputStream.getNextEntry();
    }
    zipInputStream.close();
  }

  // 如果父目录不存在则创建
  private static void mkdir(File file) {
    if (null == file || file.exists()) {
      return;
    }
    mkdir(file.getParentFile());
    file.mkdir();
  }

  public static void main(String[] args) throws Exception {
    String zipFilePath = "D:/test.zip";
    String desDirectory = "D:/a";
    unzip(zipFilePath, desDirectory);
  }
}

四 总结

在解压缩文件过程中,主要是对流的读取操作,注意进行异常处理,以及关闭流。
web应用中,通过接口可以实现文件上传下载,对应的我们只要把压缩后的文件,写入到response.getOutputStream()输出流即可。
解压缩文件时,注意空文件夹的处理。

此演示项目已上传到Github,如有需要可自行下载,欢迎 Star 。 https://github.com/LucioChn/common-utils

以上就是Java 如何实现解压缩文件和文件夹的详细内容,更多关于Java 解压缩文件和文件夹的资料请关注我们其它相关文章!

(0)

相关推荐

  • java 如何读取远程主机文件

    我就废话不多说了,大家还是直接看代码吧~ package com.cloudtech.web.util; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import com.cloudtech.web.entity.Role; public class

  • java自定义ClassLoader加载指定的class文件操作

    继承ClassLoader并且重写findClass方法就可以自定义一个类加载器,具体什么是类加载器以及类加载器的加载过程与顺序下次再说,下面给出一个小demo 首先定义一个类,比如MyTest,并且将其编译成class文件,然后放到一个指定的文件夹下面,其中文件夹的最后几层就是它的包名,这里我将这个编译好的类放到 : /Users/allen/Desktop/cn/lijie/MyTest.class package cn.lijie; public class MyTest { public

  • java中Servlet程序下载文件实例详解

    对于一些普通的文件下载,想必大家都会去点击默认的链接进行资料获取.效率慢是一个方面,有时候下载的过程并不顺序.在学习了python中的一些程序后,我们可以选择使用Servlet进行文件的下载.下面我们先就Servlet进行简单的说明,然后带来有关的下载文件代码实例. 1.说明 Servlet是Sun公司开发的用于交互式地浏览和生成数据,生成动态Web的技术.狭义的Servlet是指Java语言实现的一个接口.但一般情况下,我们把实现了Servlet接口的Java程序叫做Servlet 2.使用s

  • Java基础之文件概述

    一.基本概念和常识 下面,我们先介绍一些基本概念和常识,包括二进制思维.文件类型.文本文件的编码.文件系统和文件读写等. 1.1 二进制思维 为了透彻理解文件,我们首先要有一个二进制思维. 所有文件, 不论是可执行文件.图片文件.视频文件.Word文件.压缩文件.txt 文件,都没什么可神秘的,它们都是以0和1的二进制形式保存的.我们 所看到的图片.视频.文本,都是应用程序对这些二进制的解析结果. 作为程序员,我们应该有一个编辑器,能查看文件的二进制形式, 比如UltraEdit,它支持以十六进

  • Java(TM) Platform SE binary 打开jar文件的操作

    直接用javaw.exe想打开aspectj-1.9.4.jar安装aspectJ 选Java™ Platform SE binary提示JVM虚拟机打不开 可能是java的配置出了点问题,这里不想重新去配置java,直接用cmd用指令打开 成功打开AspectJ安装程序开始安装 补充:Java(TM) pPlatform SE binary已停止工作(解决办法) 问题描述: Java™ pPlatform SE binary已停止工作解决办法 问题事件名称:APPCRASH 早起清理了一下电脑

  • java不解压直接读取压缩包中文件的实现方法

    前言 最近写了个上传压缩包,将压缩包中的图片保存的接口,所以翻了翻网上文件流操作的博客,总结了一个不用解压,直接读取文件的方法 上代码 @RequestMapping(value = "packageUpload") public void packageUpload(HttpServletRequest request, HttpServletResponse response) { File file = null; try { MultipartHttpServletReques

  • IntelliJ IDEA创建普通的Java 项目及创建 Java 文件并运行的教程

    最近突然看到这篇几年前随手记录的文章,居然浏览量那么高.看来很多小伙伴也开始从 Eclipse 转到 IDEA,这里为了让大家更好的掌握 IDEA 的使用,我建议大家可以看看下面这个 IDEA 教程. IDEA 教程:IntelliJ-IDEA-Tutorial 首先,确保 IDEA 软件正确安装完成,Java 开发工具包 JDK 安装完成. IntelliJ IDEA下载地址:https://www.jetbrains.com/idea/download/#section=windows JD

  • Java IO流学习总结之文件传输基础

    一.Java IO流总览 二.File类 2.1 常用API package pkg1; import java.io.File; import java.io.IOException; /** * @author Administrator * @date 2021/4/2 */ public class FileDemo { public static void main(String[] args) { // 了解构造函数,可查看API File file = new File("d:\\

  • JavaWeb实现文件的上传与下载

    JavaWeb实现文件的上传与下载,供大家参考,具体内容如下 第一步:导包 导入commons-fileupload-1.3.3.jar和commons-io-2.4.jar两个依赖包 第二步:编写前端页面 1.提交页面 index.jsp <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ tagl

  • Java定时调用.ktr文件的示例代码(解决方案)

    1.Maven依赖 <!-- Kettle --> <dependency> <groupId>pentaho-kettle</groupId> <artifactId>kettle-core</artifactId> <version>7.1.0.0-12</version> </dependency> <dependency> <groupId>pentaho-kettl

  • 浅谈javap命令拆解字节码文件

    目的拆解分析反编译字节码 解析成人能够理解的结构 ,然后再对字节码文件进一步分析 源代码 public class test { private static int classV =2; public static void main(String[] args) { classV =200; int localV =4; localV =400; } } 二进制 idea bin_ed插件查看. 看不懂 那就使用人能看的懂的汇编语言查看类文件结构和代码指令. javap 指令和选项 0:无选

  • Java实战之简单的文件管理器

    示例图 可以在指定目录下实现文件的创建.文件夹的创建.文件的复制.粘贴.删除.重命名.返回上一级目录.以及不同设备之间文件的发送 完整代码 package com.atguitu.java; public class FileDemo { public static void main(String[] args) { FileSystem fs = new FileSystem(); fs.start(); } } package com.atguitu.java; import java.a

随机推荐