I/O流


我们以前运行Java程序的时候,数据都是存在变量、数组、对象中,这些都是暂时存在的,程序运行时才会存在内存中,属于虚拟空间,程序关闭后这些数据将不复存在。为了能够永久的保存程序中创建的数据,需要将数据保存到磁盘文件中,这样在其他程序中可以通过读取来使用。Java提供了I/O技术,可以将数据保存到文本、二进制、Zip压缩文件中,这样就可以永久保存了。事实上,在实际开发中,对I/O的使用尤为频繁,十分熟练的掌握I/O技术非常有必要。

1、File类

File类是java.io包中唯一代表磁盘文件本身的对象。可以通过调用File类中的一些方法,实现创建、删除、重命名等操作。File类的对象主要用来获取文件本身的一些信息,如文件所在的目录、文件长度、文件读写权限等。数据流可以将数据写入到文件中,文件是数据流最常用的数据媒体。Java中文件路径分隔符使用/\\。文件可以存储字节或字符,目录不能直接存储字节或字符,只能存储子目录,文件可以被读取或写入,而目录不支持读写操作,只能建立、删除及获取其中包含的子目录及文件。

api文档说明如下:
在这里插入图片描述

1.1、构造方法

File类的构造方法如下:
在这里插入图片描述
以下通过代码说明:

    public static void main(String[] args) {
        String path = "F:/Demo/";
        File f0 = new File(path);
        boolean b0 = f0.exists();
        System.out.println("目录存在吗?" + b0);
        // 基于父目录构建文件对象
        File f2 = new File(path, "test1");
        f2.mkdir();
        // 基于父对象构建文件对象
        File f3 = new File(f0, "test2");
        f3.mkdir();
        System.out.println("目录创建完成,请查看!");
    }

F盘是有一个空的目录Demo的,执行这个程序:
在这里插入图片描述
在这里插入图片描述
创建成功了。

1.2、常用方法

File类提供的所有方法如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
常用的方法说明如下:

  • canExecute():文件是否可执行,返回布尔值。
  • canRead():文件是否可读,返回布尔值。
  • canWrite():文件是否可写入,返回布尔值。
  • createNewFile():文件不存在时,此方法可以创建一个物理文件,返回布尔值。
  • delete() :文件存在时,删除文件,返回布尔值。
  • exists() :文件是否文件,返回布尔值。
  • getAbsoluteFile():返回绝对路径下的文件对象。
  • getAbsolutePath():获取文件的绝对路径,返回的是字符串。
  • getName():获取文件的名称。
  • getParent():获取文件所在目录的绝对路径,返回字符串。
  • getParentFile():获取文件所在目录的文件对象。
  • getPath():获取文件路径。
  • isDirectory():判断是否是目录,返回布尔值。
  • isFile():判断是否是文件,返回布尔值。
  • isHidden():判断是否为隐藏文件,返回布尔值。
  • lastModified():返回文件的上次修改时间,返回的是long型。.
  • length():返回文件的长度,返回long型。
  • list():返回一个字符串数组,命名由此抽象路径名表示的目录中的文件和目录。
  • listFiles():返回目录列表,返回的是File类型数组。
  • mkdir():创建单级目录,返回布尔值。
  • mkdirs():创建多级目录,返回布尔值。
  • renameTo(File dest):文件重命名,返回布尔值。

其他方法可以参考File类的api文档。

以下代码测试方法:

    public static void main(String[] args) throws IOException {
        File f0 = new File("F:/Demo/test1");
        File f2 = new File(f0, "/t1");
        if (!f2.exists()) {
            f2.mkdir();// 创建单级目录
            System.out.println("目录创建完成!");
        }
        File f3 = new File(f2, "/t2/t3");
        if (!f3.exists()) {
            f3.mkdirs();// 创建多级目录
            System.out.println("目录创建完成!");
        }
        File f4 = new File(f3, "/测试.txt");
        if (!f4.exists()) {
            f4.createNewFile();// 创建物理文件
            System.out.println("文件创建完成!");
        }
        System.out.println("文件是否可执行?" + f4.canExecute());
        System.out.println("文件是否可读?" + f4.canRead());
        System.out.println("文件是否可写?" + f4.canWrite());
        System.out.println(f4.getAbsoluteFile().getPath());
        // 获取路径
        System.out.println("文件绝对路径:" + f4.getAbsolutePath());
        System.out.println("文件路径:" + f4.getPath());
        System.out.println("文件名称:" + f4.getName());
        System.out.println("文件大小:" + f4.length());
        System.out.println("文件的父目录路径是:" + f4.getParent());
        System.out.println(f4.getParentFile().getAbsolutePath());
        System.out.println("是文件吗?" + f4.isFile());
        System.out.println("是目录吗?" + f4.isDirectory());
        long t = f4.lastModified();
        Date d0 = new Date(t);
        SimpleDateFormat sdf = new SimpleDateFormat("YYYY年MM月dd日 HH时mm分ss秒");
        String dateStr = sdf.format(d0);
        System.out.println("文件的上次修改时间是:" + dateStr);
        System.out.println("文件时隐藏的吗?" + f4.isHidden());
        // 重命名
        File f5 = new File(f3, "ceshi.txt");
        f4.renameTo(f5);
        // 创建10个文件
        for (int i = 1; i <= 10; i++) {
            File file = new File(f3, i + ".txt");
            if (!file.exists()) {
                file.createNewFile();
            }
        }
        // 获取目录文件列表名称数组
        String[] strs = f3.list();
        System.out.println("此目录下的所有文件名称:");
        for (String s : strs) {
            System.out.println(s);
        }
        // 获取目录下的所有文件列表
        File[] files = f3.listFiles();
        System.out.println("现在文件列表数目:" + files.length);
        System.out.println("文件信息如下:");
        for (File f : files) {
            System.out.print("文件名称:" + f.getName() + "\t" + "文件大小:" + f.length() + "字节" + "\n");
        }
        // 删除目录下的所有文件
        for (File f : files) {
            if (f.isFile()) {
                f.delete();
            }
        }
        // 重新获取文件列表
        File[] files2 = f3.listFiles();
        System.out.println("现在文件列表数目:" + files2.length);
    }

执行:
在这里插入图片描述
在这里插入图片描述
I/O流大部分是基于File文件对象来进行的,因此,要熟练掌握File对象的构造方法和常用方法。

2、流的概念

流是一组有序的数据序列。 按照方向可以分为输入流输出流。I/O即Input/Output的缩写,I/O流提供了一条通道程序,可使用这条通道把源中的字节序列输送到目的地。程序的源和目的地可以是键盘、鼠标、内存或显示器等,一般是磁盘文件。

输出模式:

在这里插入图片描述
从指向源的输入流中读取源中的数据,然后将数据显示出来,源可以是文件、网络、压缩包或其他数据源。

输出模式:

在这里插入图片描述
输出流的指向是数据要到达的目的地,程序通过向输出流中写入数据并把它传递到目的地,输出流的目标同样可以是文件、网络、压缩包和其输出目标。

整个I/O流的层级结构如下:
在这里插入图片描述

2.1、输入流

Java中定义了很多专门负责各种方式的输入和输出,这些类在java.io包中。其中,所有输入流类都是抽象类InputStream或抽象类Reader的子类。输入流是指建立在文件或其他文件流上的用来对目标文件进行读取的文件流,输入流只能对文件进行读取,读取二进制文件使用字节输入流,读取文本文件使用字符输入流。

2.1.1、字节输入流

InputStream类与字节输入流有关,是所有字节输入流的父类。字节流用来处理二进制文件。InputStream的层次结构如下:
在这里插入图片描述
标记的是用的较多的。

api说明如下:
在这里插入图片描述
提供的方法如下:
在这里插入图片描述
下面通过一个例子来说明。我想读取电脑上的一张图片:

    public static void main(String[] args) throws IOException {
        // 基于要读取的文件建立文件对象
        File file = new File("F:/Users/Administrator/Desktop/time.jpg");
        InputStream inputStream = null;
        try {
            // 基于文件对象建立输入流
            inputStream = new FileInputStream(file);
            // 字节数组用作缓冲区
            byte[] bys = new byte[(int) file.length()];
            // 读取
            int len = inputStream.read(bys);
            System.out.println("文件大小是:" + (len / 1024) + "KB");
            String s = new String(bys, 0, len);
            System.out.println("文件内容:" + s);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 关闭流
            inputStream.close();
        }

    }

控制台:
在这里插入图片描述
内容肯定会乱码,图片时二进制的,用字符串显示是一定会乱码的。

原图片:
在这里插入图片描述
然后读取一个文本文件,修改一行代码:

File file = new File("F:/Users/Administrator/Desktop/节气.txt");

再执行,控制台:
在这里插入图片描述
原文件内容:
在这里插入图片描述
读取正确,并且没乱码。

2.1.2、字符输入流

Reader类与字符输入流有关,字符流是专门用来处理文本文件的,在处理字符串时简化了编程。Reader类是所有字符输入流的父类,层级结构如下:
在这里插入图片描述
标记的是用的比较多的。

api说明如下:
在这里插入图片描述
提供的方法如下:
在这里插入图片描述
字符输入流一般用来读取文本文件的。通过一个例子说明:

    public static void main(String[] args) throws IOException {
        // 基于要读取的文件建立文件对象
        File file = new File("F:/Users/Administrator/Desktop/节气.txt");
        Reader reader = null;
        try {
            // 基于文件对象建立输入流
            reader = new FileReader(file);
            // 字符数组用作缓冲区
            char[] chs = new char[(int) file.length()];
            // 读取
            int len = reader.read(chs);
            System.out.println("文件大小是:" + len + "字节");
            String s = new String(chs, 0, len);
            System.out.println("文件内容:" + "\n" + s);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 关闭流
            reader.close();
        }

    }

控制台:
在这里插入图片描述
缓冲流:

下面使用带缓存区的输入流BufferedReader来读取文本文件,简称缓冲流,其他的类似。其实缓冲流只是对普通的输入流进行了带缓存区的包装,从而达到了性能优化的目的。

api说明:
在这里插入图片描述
构造方法:
在这里插入图片描述
构造方法接收一个输入流对象作为参数。

提供的方法:
在这里插入图片描述
与Reader提供的方法基本相同,以下通过例子进行说明:

    public static void main(String[] args) throws IOException {
        // 基于要读取的文件建立文件对象
        File file = new File("F:/Users/Administrator/Desktop/节气.txt");
        Reader reader = null;
        BufferedReader br = null;
        try {
            // 基于文件对象建立输入流
            reader = new FileReader(file);
            // 基于文件对象创建缓存流
            br = new BufferedReader(reader);
            // 逐行读取
            String s = null;
            int i = 1;
            while ((s = br.readLine()) != null) {
                System.out.println("第" + i + "行内容:" + s);
                i++;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 关闭流
            br.close();
            reader.close();
        }

    }

控制台:
在这里插入图片描述
其实可以看到,普通的输入流需要一个数组容器来充当缓冲区,而有缓冲流之后,不需要这个数组容器了,数组容器的数据暂存功能交给缓冲流来解决了。

2.2、输出流

输出流类都是OutputStream类抽象类或Writer类的子类。输出流是指建立在文件或其他文件流上的用来对目标文件进行写入的文件流,输出流只对目标文件进行写入操作,写入二进制文件使用字节输出流,写入文本文件使用字符输出流。

2.2.1、字节输出流

OutputStream类是所有字节输出流的父类。OutputStream类的层级结构如下:
在这里插入图片描述
标记的是使用的较多的。

api中说明如下:
在这里插入图片描述
提供的方法如下:
在这里插入图片描述
以下通过一个例子来说明。我想将一首歌写入到桌面上:
在这里插入图片描述
将标记的这首歌通过输入流读取,然后通过输出流传输到桌面保存。

代码如下:

    public static void main(String[] args) throws IOException {
        // 基于要读取的文件建立文件对象
        File file = new File("F:/音乐文件/爱江山更爱美人.mp3");
        if (!file.exists()) {
            System.out.println("文件不存在!");
            // 结束程序
            System.exit(0);
        }
        // 音频文件用字节输入流
        InputStream inputStream = new FileInputStream(file);
        // 缓冲流
        BufferedInputStream bis = new BufferedInputStream(inputStream);
        // 二进制数据暂存区
        byte[] bys = new byte[(int) file.length()];
        // 将数据读取到数组中
        bis.read(bys);
        // 基于要写入的文件建立对象
        File file2 = new File("F:/Users/Administrator/Desktop/" + file.getName());
        if (!file2.exists()) {
            file2.createNewFile();
        }
        // 音频文件,建立字节输出流
        OutputStream outputStream = new FileOutputStream(file2);
        // 缓冲流
        BufferedOutputStream bos = new BufferedOutputStream(outputStream);
        // 写入数组中的数据
        bos.write(bys);
        // 关闭流
        bos.close();
        outputStream.close();
        bis.close();
        inputStream.close();
        System.out.println("写入完毕,请查看!");
    }

执行,控制台:
在这里插入图片描述
桌面:
在这里插入图片描述
测试文件是否可以播放:
在这里插入图片描述
可以播放,没问题。

2.2.2、字符输出流

Writer类是所有字符输出流的父类。Writer类的层级结构如下:
在这里插入图片描述
标记的是用的比较多的。

api说明如下:
在这里插入图片描述
提供的方法如下:
在这里插入图片描述
示例如下:

    public static void main(String[] args) throws IOException {
        // 基于要读取的文件建立文件对象
        File file = new File("F:/Users/Administrator/Desktop/节气.txt");
        // 字符输入流
        Writer writer = new FileWriter(file);
        // 要写入的内容
        String s = "大雪过后,一定立春!";
        // 直接写入
        writer.write(s);
        System.out.println("写入完毕,请查看!");
        // 关闭流
     

先看看原文本文件的内容:
在这里插入图片描述
执行程序,控制台:
在这里插入图片描述
再查看文本的内容:
在这里插入图片描述
可以看到,将原文本内容覆盖了,并不是追加到后面,如果不想覆盖的话,修改这一行:

Writer writer = new FileWriter(file,true);

实例化流对象的时候,加一个布尔参数,不写的话它是默认为false的,也就是会覆盖原文件,这里参数为true,即原文件内容中追加,不会覆盖。

修改完毕后,再执行:
在这里插入图片描述
查看文件内容:
在这里插入图片描述
发现是追加的,没问题。

3、文件复制

其实上面已经演示过了,先用输入流读取源,再用输出流输出到指定路径就行了。

在这里插入图片描述
现在我想将桌面这个JWT目录复制到F盘根目录下,代码如下:

    public static void main(String[] args) throws IOException {
        // 要复制的目录
        File file = new File("F:/Users/Administrator/Desktop/JWT");
        if (!file.exists()) {
            System.out.println("不存在,无法复制!");
            System.exit(0);
        }
        // 输出文件对象
        File file2 = new File("F:/JWT");
        // 获取目录下的文件列表
        File[] files = file.listFiles();
        System.out.println("开始复制......");
        for (int i = 0; i < files.length; i++) {
            // 输入流
            InputStream inputStream = new FileInputStream(files[i]);
            byte[] bys = new byte[(int) files[i].length()];
            // 读取数据进数组
            inputStream.read(bys);
            if (!file2.exists()) {
                file2.mkdir();
            }
            // 输出流
            OutputStream outputStream = new FileOutputStream(new File(file2, "/" + files[i].getName()));
            // 写入
            outputStream.write(bys);
            // 关闭流
            outputStream.close();
            inputStream.close();
        }
        System.out.println("复制完成!");
    }

需要注意的是流对象只能根据文件对象来建立,而不能根据目录对象建立。

执行:
在这里插入图片描述
查看F盘:
在这里插入图片描述
打开查看内容:
在这里插入图片描述
没问题。

4、文件移动

这个只需要在文件复制的基础上,删除掉原文件就可以了。
在这里插入图片描述
我现在想把桌面文件夹中的这个文件移动到桌面上,代码如下:

    public static void main(String[] args) throws IOException {
        // 要移动的文件
        File file = new File("F:/Users/Administrator/Desktop/SpringBoot/springboot笔记.txt");
        if (!file.exists()) {
            System.out.println("不存在,无法移动!");
            System.exit(0);
        }
        // 输出文件对象的目录
        File file2 = new File("F:/Users/Administrator/Desktop/");
        System.out.println("开始移动......");
        // 输入流
        InputStream inputStream = new FileInputStream(file);
        byte[] bys = new byte[(int) file.length()];
        // 读取数据进数组
        inputStream.read(bys);
        if (!file2.exists()) {
            file2.mkdir();
        }
        // 输出流
        OutputStream outputStream = new FileOutputStream(new File(file2, "/" + file.getName()));
        // 写入
        outputStream.write(bys);
        // 关闭流
        outputStream.close();
        inputStream.close();
        // 将原文件删除掉
        if (file.exists()) {
            file.delete();
        }
        System.out.println("移动完成!");
    }

执行,控制台:
在这里插入图片描述
原文件夹:
在这里插入图片描述
文件没了,再看桌面:
在这里插入图片描述
在桌面,说明移动是成功的。移动其实就是输入流读取源文件,输出流将读取内容输出到指定位置,然后删掉源文件。

5、数据输入/输出流

其实前面的都是文件输入/输入流的内容,因此基本上都是针对文件的,用的最多的就是文件流。使用文件流的时候,我们必须要清楚我们操作的文件是字符流还是字节流,从而选用最合适的输入输出流对象。这里测试一些数据的输入和输出流。

数据输入流:

api文档说明:
在这里插入图片描述
构造方法:
在这里插入图片描述
构造方法接收一个输入流对象作为参数。

提供的方法如下:
在这里插入图片描述
在这里插入图片描述
代码测试:

    public static void main(String[] args) throws IOException {
        // 要读取的文件对象
        File file = new File("F:/Users/Administrator/Desktop/节气.txt");
        if (!file.exists()) {
            System.out.println("无法读取,文件不存在!");
            System.exit(0);
        }
        // 文件输入流
        InputStream inputStream = new FileInputStream(file);
        // 基于文件输入流建立数据输入流
        DataInputStream dataInputStream = new DataInputStream(inputStream);
        byte[] bys = new byte[(int) file.length()];
        // 读取内容到数组中
        dataInputStream.read(bys);
        System.out.println("读取到的内容如下:");
        System.out.println(new String(bys, 0, bys.length));
        //关闭流
        dataInputStream.close();
        inputStream.close();
    }

控制台:
在这里插入图片描述
因为使用readUTF方法读取的时候报错了,所以这里还是用read方法读取。

数据输出流:

api文档说明:
在这里插入图片描述
构造方法如下:
在这里插入图片描述
构造方法接收一个输入流对象作为参数。

提供的方法如下:
在这里插入图片描述
在这里插入图片描述
代码测试:

    public static void main(String[] args) throws IOException {
        // 要写入的文件对象
        File file = new File("F:/Users/Administrator/Desktop/节气.txt");
        if (!file.exists()) {
            file.createNewFile();
        }
        // 文件输出流
        OutputStream outputStream = new FileOutputStream(file,true);
        // 基于文件输出流建立数据输出流
        DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
        String res = "看那青山荡漾在水上,看那晚霞吻着夕阳!";
        // 写入
        dataOutputStream.writeBytes(res);
        System.out.println("写入完毕!");
        // 关闭流
        dataOutputStream.close();
        outputStream.close();
    }

控制台:
在这里插入图片描述
查看文件内容:
在这里插入图片描述
乱码了,修改这一行代码:

dataOutputStream.writeUTF(res);

再执行,查看文件内容:
在这里插入图片描述
还是乱码,再修改一下:
在这里插入图片描述
还是乱码。所以,还是使用文件流比较好。

6、序列化读写

不仅可以以字节和字符形式读写文件,还可对任何java数据类型进行读写,将一个数据以Java对象的形式存储到文件中或从目标文件中获取一个Java对象的操作被称为序列化操作,能被序列化的对象即为序列化对象。任何实现Serializable接口的类,被视为序列化类,此类的实例即是一个序列化对象。序列化对象可通过序列化输入和输出流进行读写操作,通常序列化类必须提供一个公共无参的构造器,在反序列化中JVM使用公共无参构造器创建Java对象。

6.1、序列化

序列化指将一个Java对象通过流写入到文件中保存。需要用到ObjectOutputStream对象输出流,此流基于OutputStream输出流。

api文档如下:
在这里插入图片描述
构造方法:
在这里插入图片描述
一般第二种构造方法用的比较多,接收一个输出流对象作为参数。其他方法可以查看api文档说明。

以下用代码示例。实体类如下:

 * 类需要实现Serializable接口完成序列化
 */
public class Student implements Serializable {

    private static final long serialVersionUID = -1584041033462631788L;

    private String name;

    private String sex;

    public Student() {

    }

    public Student(String name, String sex) {
        this.name = name;
        this.sex = sex;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

}

特别要注意这行:

private static final long serialVersionUID = -1584041033462631788L;

如果序列号换成下面这样:

private static final long serialVersionUID = 1L;

这样序列化是没问题的,但是反序列化读取的时候会报找不到序列号的错误。

测试:

    public static void main(String[] args) throws IOException {
        // 要写入的文件对象
        File file = new File("F:/Users/Administrator/Desktop/Student.DATA");
        if (!file.exists()) {
            file.createNewFile();
        }
        Student s0 = new Student("云过梦无痕", "男");
        // 文件输出流
        OutputStream outputStream = new FileOutputStream(file, true);
        // 基于文件输出流建立对象输出流
        ObjectOutputStream oos = new ObjectOutputStream(outputStream);
        // 写入对象
        oos.writeObject(s0);
        System.out.println("写入完毕!");
        // 关闭流
        oos.close();
        outputStream.close();
    }

执行,控制台:
在这里插入图片描述
查看桌面:
在这里插入图片描述
打开查看内容:
在这里插入图片描述
乱码,因为保存的是对象。

6.2、反序列化

反序列化指通过流读取文件中保存的Java对象。需要用到ObjectIntputStream对象输入流,此流基于InputStream输出流。

api说明:
在这里插入图片描述
构造方法:
在这里插入图片描述
第二种用的比较多,接收一个输入流对象作为参数。其他方法可以参考api文档。

测试:

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        // 要读取文件对象
        File file = new File("F:/Users/Administrator/Desktop/Student.DATA");
        if (!file.exists()) {
            System.out.println("文件不存在!");
            System.exit(0);
        }
        // 文件输入流
        InputStream inputStream = new FileInputStream(file);
        // 基于文件输入流建立对象输入流
        ObjectInputStream ois = new ObjectInputStream(inputStream);
        // 读取,返回的是一个Object对象
        Object obj = ois.readObject();
        // 转换类型
        Student s = (Student) obj;
        System.out.println("姓名:" + s.getName());
        System.out.println("性别:" + s.getSex());
        // 关闭流
        ois.close();
        inputStream.close();

    }

控制台:
在这里插入图片描述
Java对象正确读取出来了。

7、ZIP压缩/解压缩

我们常常需要将文进行压缩打包,然后将压缩包发给别人。或者我们在网上下载资源的时候,很多情况下下载下来的都是压缩包。比如zip、rar格式的,那么在Java中怎么将文件进行压缩和解压呢?

7.1、压缩

压缩需要用到输入流、输出流以及压缩输出流ZipOutputStream。关于ZipOutputStream类,api中说明如下:
在这里插入图片描述
构造方法如下:
在这里插入图片描述
构造方法接收一个输出流对象作为参数,可以设置编码格式。

提供的方法如下:
在这里插入图片描述
标记的方法使用的较多,下面用一个例子说明。

代码如下:

    /*
     * 定义压缩文件的方法 
     * dicPath是压缩的目录名称 
     * zipFileName是压缩文件名称
     */
    public static void zip(String dicPath, String zipFileName) throws IOException {
        File zipFile = new File(zipFileName);
        if (!zipFile.exists()) {
            zipFile.createNewFile();
        }
        // 基于压缩文件对象的文件输出流
        OutputStream outputStream = new FileOutputStream(zipFile);
        // 基于文件输出流的压缩输出流
        ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream);
        // 输入流
        InputStream inputStream = null;
        // ZipEntry对象
        ZipEntry zipEntry = null;
        File dic = new File(dicPath);
        // 如果是文件夹
        if (dic.isDirectory()) {
            // 获取目录下的所有文件列表
            File[] files = dic.listFiles();
            // 如果目录是空的,直接退出程序
            if (files.length == 0) {
                System.out.println("当前目录无文件!");
                System.exit(0);
            }
            System.out.println("压缩中............");
            for (int i = 0; i < files.length; i++) {
                // 每个文件创建输出流
                inputStream = new FileInputStream(files[i]);
                // 实例化ZipEntry对象
                zipEntry = new ZipEntry(files[i].getName());
                // 放入压缩输出流中
                zipOutputStream.putNextEntry(zipEntry);
                // 数据暂存字节数组
                byte[] bys = new byte[(int) files[i].length()];
                // 将数据读进数组
                inputStream.read(bys);
                // 写入数组中的数据
                zipOutputStream.write(bys);
                // 关闭流
                inputStream.close();
            }
        } else {// 如果是单文件
            System.out.println("压缩中............");
            inputStream = new FileInputStream(dic);
            zipEntry = new ZipEntry(dic.getName());
            zipOutputStream.putNextEntry(zipEntry);
            byte[] bys = new byte[(int) dic.length()];
            inputStream.read(bys);
            zipOutputStream.write(bys);
            inputStream.close();
        }
        // 关闭输出流
        zipOutputStream.closeEntry();
        zipOutputStream.close();
        outputStream.close();
        System.out.println("压缩完成,请查看!");
    }

测试:

    public static void main(String[] args) {
        try {
            MethodsUtil.zip("F:/音乐文件", "F:/Users/Administrator/Desktop/音乐文件.zip");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

我想压缩的是这个目录:
在这里插入图片描述
执行程序:
在这里插入图片描述
查看桌面:
在这里插入图片描述
在这里插入图片描述
是成功的。

再压缩一个空目录:
在这里插入图片描述
修改这行代码:

MethodsUtil.zip("F:/Users/Administrator/Desktop/test", "F:/Users/Administrator/Desktop/test.zip");

执行:
在这里插入图片描述
因为空目录不支持压缩!

再测试压缩一个单文件:

MethodsUtil.zip("F:/Users/Administrator/Desktop/节气.txt", "F:/Users/Administrator/Desktop/节气.rar");

在这里插入图片描述
桌面:
在这里插入图片描述
在这里插入图片描述
OK,压缩功能没问题,可以压缩单个文件,也可以压缩文件夹(但是文件夹里不能有子目录),不支持空目录压缩,可以在此基础上,修改程序,使其支持文件夹下有子目录的文件压缩。

7.2、解压缩

解压缩需要用到输入流、输出流以及压缩输入流ZipInputStream。关于ZipInputStream类,api中说明如下:
在这里插入图片描述
构造方法如下:
在这里插入图片描述
接收一个输入流对象作为参数,可是设置编码格式。

提供的方法如下:
在这里插入图片描述
标记的方法是用的比较多的。下面通过例子说明。

代码:

    /*
     * 定义一个方法,用于解压缩文件
     *  zipFileName是压缩文件的名称 
     *  dicName是解压出来的目录的名称
     */
    public static void decompress(String zipFileName, String dicName) throws ZipException, IOException {
        File file = new File(zipFileName);
        if (!file.exists()) {
            System.out.println("压缩文件不存在!");
            System.exit(0);
        }
        File dic = new File(dicName);
        if (!dic.exists()) {
            dic.mkdirs();
        }
        // ZipFile对象
        ZipFile zipFile = new ZipFile(file);
        // 输入流
        InputStream inputStream = new FileInputStream(file);
        // 压缩输入流
        ZipInputStream zipInputStream = new ZipInputStream(inputStream);
        // 获取zipEntry
        ZipEntry zipEntry = null;
        System.out.println("开始解压......");
        while ((zipEntry = zipInputStream.getNextEntry()) != null && !zipEntry.isDirectory()) {
            File f = new File(dic, "/" + zipEntry.getName());
            if (!f.exists()) {
                f.createNewFile();
            }
            // 输出流
            OutputStream outputStream = new FileOutputStream(f);
            // 输入流读取压缩文件中的文件
            InputStream in = zipFile.getInputStream(zipEntry);
            int len = 0;
            while ((len = in.read()) != -1) {
                // 写入
                outputStream.write(len);
            }
            // 关闭流
            in.close();
            outputStream.close();
        }
        // 关闭流
        inputStream.close();
        zipInputStream.closeEntry();
        zipInputStream.close();
        System.out.println("解压完成!");
    }

测试:

    public static void main(String[] args) {
        try {
            MethodsUtil.decompress("F:/Users/Administrator/Desktop/音乐文件.zip", "F:/Users/Administrator/Desktop/音乐");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

执行:
在这里插入图片描述
桌面:
在这里插入图片描述
在这里插入图片描述
播放:
在这里插入图片描述
解压缩也没问题。这里的方法只支持压缩文件下没有目录的,可以在此基础上做修改以支持。这里就不再改了。

8、从网络上下载文件

需要用到URL和HttpURLConnection,以及输入输出流,以下直接演示:

    /*
     * 定义一个方法,下载网络资源
     * resourceUrl 资源URL
     * dicPath  存放目录
     * fileName 文件名称
     */
    public static void downloadResourceFromNet(String resourceUrl, String dicPath, String fileName) 
            throws IOException {
        //URL对象
        URL url = new URL(resourceUrl);
        //获取HttpURLConnection对象
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        // 获取输出流
        InputStream inputStream = connection.getInputStream();
        int len = 0;
        File dic = new File(dicPath);
        if (!dic.exists()) {
            dic.mkdirs();
        }
        File file = new File(dic, "/" + fileName);
        if (!file.exists()) {
            file.createNewFile();
        }
        // 输入流
        OutputStream outputStream = new FileOutputStream(file);
        System.out.println("开始下载......");
        // 边读取边写入
        while ((len = inputStream.read()) != -1) {
            outputStream.write(len);
        }
        // 关闭流
        outputStream.close();
        inputStream.close();
        System.out.println("下载完成!");
    }

测试下载一张图片:

    public static void main(String[] args) {
        try {
            MethodsUtil.downloadResourceFromNet(
                    "https://p2.ssl.qhimgs1.com/sdr/400__/t01e645b0bf397e5b08.jpg", 
                    "F:/Users/Administrator/Desktop/图片",
                    "狗狗.jpg");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

在这里插入图片描述
查看:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
测试下载QQ软件:

    public static void main(String[] args) {
        try {
            MethodsUtil.downloadResourceFromNet(
                    "https://b81b7bf5ece3dd9cbade582c7c3a682d.dlied1.cdntips.net/dlied1.qq.com/qqweb/PCQQ/PCQQ_EXE/PCQQ2020.exe?mkey=6007da73b75f91d0&f=8fa4&cip=183.95.183.37&proto=https&access_type=$header_ApolloNet", 
                    "F:/Users/Administrator/Desktop/图片",
                    "腾讯QQ.exe");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

执行:
在这里插入图片描述
查看:
在这里插入图片描述
在这里插入图片描述
这个文件是可执行的,说明下载成功。只要知道了要下载资源的URL地址,而且对方不设限制的话,那么使用Java进行下载是非常简单的!这里记录一下。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值