likes
comments
collection
share

Java 中的 IO 操作

作者站长头像
站长
· 阅读数 14

这是android文件操作的第二篇,主要学习怎么写入文件读取文件等相关的IO流操作。

环境说明:

  • 我学习以 JDK8 为主;
  • 我的示例都是跑在 JDK18 基础上;
  • 运行环境是 MacOS 13.3.1 (a) (22E772610a);
  • 编辑器为: IntelliJ IDEA 2023.1.2 (Community Edition) ;
  • 写于 2023-6-23 。

主要参考:

  1. 黑马Java基础教程,全面解析Java-IO流,一套通关
  2. FileOutputStream

主要学习:FileOutputStreamFileInputStream的相关api

1. IO 流简介

其中对于 IO 流的输入输出,参考对象是程序,比如输出流就是OutputStream,参考对象是程序,也就是把程序中的数据输出到文件中;而InputStream则是输入流,也就是程序从文件中读取数据的过程。

Java 中的 IO 操作

为啥需要IO流,在Java中要想将程序的数据保存到文件中或从文件中读取数据就离不开IO流,包括下载图片到本地,上传文件到服务器等等都需要。

2. IO 流的分类

IO流按文件类型分为字节字符;按流的方向分为输入输出。

Java 中的 IO 操作

3. 基本使用

按流的方向来说是输入和输出,我们就按这个维度来学习。上面说的InputStreamOutputStream是抽象类,不能直接使用,接下来会使用FileInputStreamFileOutputStream两个实现类来学习。

3.1 FileOutputStream

首先我们来看输出流(FileOutputStream),也就是将程序中的内容写入到文件中:

try {
    // 要写到文件的文本
    String str = "Hello world";
    // 实例化输出流
    FileOutputStream fos = new FileOutputStream("test.txt");
    // 将字符串转换成 byte[]
    byte[] byteStr = str.getBytes();
    // 写入文本到文件中
    fos.write(byteStr);
    // 关闭文件流
    fos.close();
} catch (IOException e) {
    e.printStackTrace();
}

从中我们可以看到总共分为5步:

  1. 准备好要写入到文件中文本;我这里是写死的字符串,实际使用中有可能是网络图片等等;
  2. 实例化输出流的对象,这里可以是路径也可以是文件路径;
  3. 将字符串转换成字节数组;
  4. 将字节数组的内容写入到文件中;
  5. 关闭文件输出流。

3.2 FileInputStream

输入流,也就是读取文件中的内容到程序中。

try {
    // 实例化输入流
    FileInputStream fis = new FileInputStream("test.txt");
    // 读取文件中的内容到字节数组中
    byte[] bytes = fis.readAllBytes();
    // 将字节数组中的内容转换成字符串
    String str = new String(bytes);
    System.out.println(str);
    // 关闭文件流
    fis.close();
} catch (IOException e) {
    e.printStackTrace();
}

从中我们可以看到总共分为4步:

  1. 实例化输入流的对象,这里可以是路径也可以是文件路径;
  2. 读取文件中的文件内容到字节数组中;
  3. 使用拿到的数据,我这里就是转换成字符串然后打印;
  4. 关闭文件输入流。

4. FileOutputStream详情

这里对暴露的相关 api 都会详细说明,并给出示例。

4.1 构造方法

构造函数官方描述和翻译我的理解
FileOutputStream(File file)Creates a file output stream to write to the file represented by the specified File object.通过指定的File对象创建一个文件输出流,用于向该文件写入数据。文件输出流是一种用于写入文件的Java IO流。File对象表示一个文件或目录的路径名。指定File对象来创建输出流;如果File中的文件存在,那么文件中的内容会被清空;如果文件中的文件不存在,在文件所属文件夹存在的情况下会新建这个文件;对于没有权限的文件,会立即抛异常。
FileOutputStream(File file, boolean append)同上跟上面相同;不同的是,如果文件存在不会清空文件中的内容。
FileOutputStream(FileDescriptor fdObj)Creates a file output stream to write to the specified file descriptor, which represents an existing connection to an actual file in the file system.创建一个文件输出流,用于向指定的文件描述符写入数据,该文件描述符代表实际文件系统中的一个实际文件。不是重点;目前也不怎么明白,将来明白了会补上的。
FileOutputStream(String name)Creates a file output stream to write to the file with the specified name.创建文件输出流以写入具有指定名称的文件。跟第一个构造函数相同,只不过这里传入的是文件路径,而上面是File对象。其余相同。
FileOutputStream(String name, boolean append)同上同上。不同在于不会删除原有的内容。

4.2 方法

方法声明官方描述和翻译我的理解
void close()Closes this file output stream and releases any system resources associated with this stream.关闭此文件输出流并释放与此流关联的所有系统资源。释放文件资源。
FileChannel getChannel()Returns the unique FileChannel object associated with this file output stream. 返回与此文件输出流关联的唯一文件 FileChannel 对象。暂时不明白,将来会补上。
FileDescriptor getFD()Returns the file descriptor associated with this stream.返回与该流关联的文件描述符。同上。
void write(byte[] b)Writes b.length bytes from the specified byte array to this file output stream. 将指定字节数组中的b.length字节写入此文件输出流。将完整的字节数组写入到文件中。
void write(byte[] b, int off, int len)Writes len bytes from the specified byte array starting at offset off to this file output stream.从指定的字节数组的偏移量处开始,将len个字节写入到文件输出流中。字节数组指定区间[off, off + len]内的写入到文件中。
void write(int b)Writes the specified byte to this file output stream.将指定字节写入此文件输出流。写一个字节的数据到文件中。

4.3 代码示例展示

这里将上面重要常用的API进行代码展示,并给出输出结果,然后再得出结论和相关的用处说明。

4.3.1 FileOutputStream(File file)

File对象的初始化可以查看 Java 中的 File 类

try {
    // 存在的文件
    File file = new File("test.txt");
    FileOutputStream fos = new FileOutputStream(file);
	// 如果文件类本身有内容,那么会被清空

    // 当是不存在的文件
    File file = new File("test1.txt");
    FileOutputStream fos = new FileOutputStream(file);
    // 会帮你创建一个 test1.txt 文件

    // 当文件所在文件夹不存在
    File file = new File("test/test1.txt");
    FileOutputStream fos = new FileOutputStream(file);
    // java.io.FileNotFoundException: test/test1.txt (No such file or directory)

    // 当是没有权限的文件
    File file = new File("test2.txt");
    FileOutputStream fos = new FileOutputStream(file);
    // java.io.FileNotFoundException: test2.txt (Permission denied)
    
    fos.close();
} catch (IOException e) {
    e.printStackTrace();
}

使用这种方式实例化的输出流,在写入内容到文件的时候直接从文件开头进行写入,具体看写入方法。

4.3.2 FileOutputStream(File file, boolean append)

跟上面相同,只不过这个实例化后,不会清空文件中的内容;在写入操作的时候是追加,也就是完整保存之前的内容。

4.3.3 FileOutputStream(FileDescriptor fdObj)

FileDescriptor的实例化看 FileDescriptor 。具体我也不是很理解,以后把这个补上:

FileOutputStream fos = new FileOutputStream(FileDescriptor.out);
fos.write("hello world".getBytes());
fos.close();

如果运行这个程序,会在终端中打印 "hello world" 。

4.3.4 FileOutputStream(String name)

这个跟4.3.1相同,可以看源码:

public FileOutputStream(String name) throws FileNotFoundException {
    this(name != null ? new File(name) : null, false);
}

4.3.5 FileOutputStream(String name, boolean append)

这个跟4.3.2相同。同样看看源码:

public FileOutputStream(String name, boolean append) throws FileNotFoundException
{
    this(name != null ? new File(name) : null, append);
}

4.3.6 void close()

关闭文件的输出流,目的是释放资源。我看到视频中不释放资源的情况下是删除不掉文件的;可是我使用我的电脑是可以的;有大佬知道的可以说一下;但无论怎样使用完以后释放资源是最可靠的。

4.3.7 FileChannel getChannel()

获取FileChannel对象,具体这里就先不展开了,以后会补上的,补上后叫“Java中的NIO操作”。

4.3.8 FileDescriptor getFD()

同上,以后会补上。

4.3.9 void write(byte[] b)

将整个字节数组中的内容写入到文件中。

// 原本我有一个文件 test.txt ,里面的内容为:123
FileOutputStream fos = new FileOutputStream("test.txt");
fos.write("wujingyue".getBytes());
fos.close();
// 现在再看文件的内容为:wujingyue

我们发现原来的内容会消失;当然这个跟写入操作没有关系,关键在于构造函数会清空文件中的内容;下面我们看另外一种构造函数进行完的结果:

// 经过上面的操作,内容变成了:wujingyue
FileOutputStream fos = new FileOutputStream("test.txt", true);
fos.write("wujing".getBytes());
fos.close();
// 再经过这次的写入操作,内容变成为:wujingyuewujing

4.3.10 void write(byte[] b, int off, int len)

将字节数组中[off, off + len]区间的内容写入到文件中。在这里就不演示两种不同的构造函数了,跟上面是相同的。

// 先看看怎样能达到 4.3.9 的效果
FileOutputStream fos = new FileOutputStream("test.txt");
byte[] strBytes = "wujingyue".getBytes();
int off = 0;
fos.write(strBytes, off, strBytes.length - off);
fos.close();

这样就跟 4.3.9 的效果相同,其实 4.3.9 的源码也差不多是这样的,下面我们看看 4.3.9 的源码:

public void write(byte[] b) throws IOException {
    writeBytes(b, 0, b.length, fdAccess.getAppend(fd));
}

也就是上面off=0同时长度为字节数组的长度。如果我们要取的区间不在[0, len]的范围,那么就会抛出异常。

FileOutputStream fos = new FileOutputStream("test.txt");
byte[] strBytes = "wujingyue".getBytes();
int off = 0;
fos.write(strBytes, off, 20);
fos.close();
// Exception in thread "main" java.lang.IndexOutOfBoundsException


FileOutputStream fos = new FileOutputStream("test.txt");
byte[] strBytes = "wujingyue".getBytes();
int off = -1;
fos.write(strBytes, off, strBytes.length - off);
fos.close();
// Exception in thread "main" java.lang.IndexOutOfBoundsException

4.3.11 void write(int b)

将一个字节的数据写入到文件中。

FileOutputStream fos = new FileOutputStream("test.txt");
fos.write(67);
fos.close();

此时打开文件发现里面是C。从ASCII码中可以看到67对应的字母就是这个。

5. FileInputStream详情

5.1 构造方法

构造函数官方描述和翻译我的理解
FileInputStream(File file)Creates a FileInputStream by opening a connection to an actual file, the file named by the File object file in the file system.通过打开到实际文件的连接来创建文件输入流,该文件由文件系统中的文件对象文件命名。创建输入流的对象,需要File所指的文件存在,而且有可读的权限。
FileInputStream(FileDescriptor fdObj)Creates a FileInputStream by using the file descriptor fdObj, which represents an existing connection to an actual file in the file system. 使用文件描述符 fdObj 创建文件输入流,该文件描述符表示与文件系统中实际文件的现有连接。不是重点;目前也不怎么明白,将来明白了会补上的。
FileInputStream(String name)Creates a FileInputStream by opening a connection to an actual file, the file named by the path name name in the file system.通过打开到实际文件的连接来创建文件输入流,该文件由文件系统中的路径名 name 命名。同第一个构造函数差不多,只不过这里直接就是文件的路径。

5.2 方法

方法声明官方描述和翻译我的理解
int available()Returns an estimate of the number of remaining bytes that can be read (or skipped over) from this input stream without blocking by the next invocation of a method for this input stream.返回可以从此输入流读取或跳过的剩余字节数的估计值,而不会被该输入流的方法的下一次调用所阻塞。文件未读取字节数;在读取的过程中有一个指针,指向的是当前读取的字节,读取一个字节指针就会移向下一个字节的位置,这个函数表示的从指针位置到文件末尾的字节数。
void close()Closes this file input stream and releases any system resources associated with the stream.关闭此文件输入流并释放与该流关联的所有系统资源。释放资源。
FileChannel getChannel()Returns the unique FileChannel object associated with this file input stream.返回与此文件输入流关联的唯一文件通道对象。暂时不明白,将来会补上。
FileDescriptor getFD()Returns the FileDescriptor object that represents the connection to the actual file in the file system being used by this FileInputStream.返回文件描述符对象,该对象表示与此文件输入流使用的文件系统中实际文件的连接。同上。
int read()Reads a byte of data from this input stream.从此输入流读取一个字节的数据。读取文件中的一个字节,当返回-1的时候表示没有可读取的内容。
int read(byte[] b)Reads up to b.length bytes of data from this input stream into an array of bytes.从此输入流中读取最多 b 长度字节的数据到字节数组中。读取文件b.length个字节内容到字节数组中,返回真实读取的字节数;文件中的内容不够时,字节数组后面的元素跟之前保持一致。
int read(byte[] b, int off, int len)Reads up to len bytes of data from this input stream into an array of bytes.从此输入流中读取最多 len 个字节的数据到字节数组中。读取文件len个字节内容,然后从字节数组下标为off处开始插入读取的内容。
long skip(long n)Skips over and discards n bytes of data from the input stream.跳过并丢弃输入流中的 n 个字节的数据。跳过文件内容的字节数。详情见5.4.11

5.3 代码示例展示

这里将上面重要常用的API进行代码展示,并给出输出结果,然后再得出结论和相关的用处说明。

5.3.1 FileInputStream(File file)

// 打开不存在的文件
File file = new File("test1.txt");
FileInputStream fis = new FileInputStream(file);
fis.close();
// java.io.FileNotFoundException: test1.txt (No such file or directory)

// 打开没有权限的文件
File file = new File("test2.txt");
FileInputStream fis = new FileInputStream(file);
fis.close();
// java.io.FileNotFoundException: test2.txt (Permission denied)

// 文件夹
File file = new File("src");
FileInputStream fis = new FileInputStream(file);
fis.close();
// java.io.FileNotFoundException: src (Is a directory)

// 普通文件
File file = new File("test.txt")
FileInputStream fis = new FileInputStream(file);
fis.close();
// 不报错,内容也不会发生改变

要想创建输入实例,要保证文件满足下面的条件:

  1. 文件存在;
  2. 有文件的可读权限;
  3. 必须是文件,不能是文件夹。

5.3.2 FileInputStream(FileDescriptor fdObj)

FileDescriptor的实例化看 FileDescriptor 。具体我也不是很理解,以后把这个补上。

5.3.3 FileInputStream(String name)

5.3.1相同,不同的是这个直接传入文件的名称即可,不需要传入File对象,其实源码也是将传入的 name实例化为File对象再传入。

public FileInputStream(String name) throws FileNotFoundException {
    this(name != null ? new File(name) : null);
}

5.3.4 int read()

// 在 test.txt 中的文本为:abcdef
FileInputStream fis = new FileInputStream("test.txt");
System.out.println(fis.read()); // 97
System.out.println(fis.read()); // 98
System.out.println(fis.read()); // 99
System.out.println(fis.read()); // 100
System.out.println(fis.read()); // 101
System.out.println(fis.read()); // 102
System.out.println(fis.read()); // -1
fis.close();

读取文件中的一个字节,如果已经到了文件末尾,那么就返回 -1 。

5.3.5 int available()

FileInputStream fis = new FileInputStream("test.txt");
System.out.println("1 read:" + fis.read()); // 1 read:97
System.out.println("1 available:" + fis.available()); // 1 available:5
System.out.println("2 read:" + fis.read()); // 2 read:98
System.out.println("2 available:" + fis.available()); // 2 available:4
System.out.println("3 read:" + fis.read()); // 3 read:99
System.out.println("3 available:" + fis.available()); // 3 available:3
System.out.println("4 read:" + fis.read()); // 4 read:100
System.out.println("4 available:" + fis.available()); // 4 available:2
System.out.println("5 read:" + fis.read()); // 5 read:101
System.out.println("5 available:" + fis.available()); // 5 available:1
System.out.println("6 read:" + fis.read()); // 6 read:102
System.out.println("6 available:" + fis.available()); // 6 available:0
System.out.println("7 read:" + fis.read()); // 7 read:-1
System.out.println("7 available:" + fis.available()); // 7 available:0
fis.close();

表示文件剩余未被读取字节数;不要理解文件剩余字节数,本身文件的内容并没有改变。

5.3.6 void close()

关闭文件的输入流,目的是释放资源。我看到视频中不释放资源的情况下是删除不掉文件的;可是我使用我的电脑是可以的;有大佬知道的可以说一下;但无论怎样使用完以后释放资源是最可靠的。

5.4.7 FileChannel getChannel()

获取FileChannel对象,具体这里就先不展开了,以后会补上的,补上后叫“Java中的NIO操作”。

5.4.8 FileDescriptor getFD()

同上,以后会补上。

5.4.9 int read(byte[] b)

// 文件中的内容为:abcdefg
FileInputStream fis = new FileInputStream("test.txt");
byte[] arr1 = new byte[1];
System.out.println(fis.read(arr1)); // 1
System.out.println(new String(arr1)); // a
byte[] arr2 = new byte[2];
System.out.println(fis.read(arr2)); // 2
System.out.println(new String(arr2)); // bc
byte[] arr3 = new byte[10];
System.out.println(fis.read(arr3)); // 4
System.out.println(new String(arr3)); // defg
fis.close();

读取文件内容到传入的字节数组中;有以下几种情况:

  1. 当字节数组小于文件内容,只读取字节数组大小的内容到字节数组中;
  2. 连续读取是基于上一次进行的,也就是上一次读取到a,那么下一次就从b开始读取;
  3. 当字节数组的大小大于文件内容时,字节数组后面将保持原样(默认是 0 );
  4. 每次读取返回的是读取的字节数,对于1总是跟字节数组大小相同,对于3小于字节数组长度。

5.4.10 int read(byte[] b, int off, int len)

// 文件的内容为:abcdefg
FileInputStream fis = new FileInputStream("test.txt");
byte[] test = new byte[20];
System.out.println(fis.read(test, 1, 2)); // 2
System.out.println(Arrays.toString(test)); // [0, 97, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

System.out.println(fis.read(test, 10, 10)); // 5
System.out.println(Arrays.toString(test)); // [0, 97, 98, 0, 0, 0, 0, 0, 0, 0, 99, 100, 101, 102, 103, 0, 0, 0, 0, 0]

System.out.println(fis.read(test, 15, 10));
System.out.println(Arrays.toString(test));
// Exception in thread "main" java.lang.IndexOutOfBoundsException
fis.close();

off + len不能大于字节数组的长度;读取文件len长度的内容,然后从字节数组off处开始插入,插入到下标off + len。以下几点说明:

  1. 如果 len 长度大于文件剩余内容大小,那么以文件剩余大小为主;
  2. off + len > test.length 会抛出异常,跟其他无关;

5.4.11 long skip(long n)

FileInputStream fis = new FileInputStream("test.txt");
System.out.println(fis.skip(2)); // 2
System.out.println((char) fis.read()); // c
System.out.println(fis.available()); // 4

System.out.println(fis.skip(5)); // 4
System.out.println(fis.read()); // -1
System.out.println(fis.available()); // 0
fis.close();

表示跳过的字节数,返回值是真实跳过的字节数。

5.4.12 byte[] readAllBytes()

就是读取所有的字节保存到字节数组中,其源码就是使用 read() 实现的。

public byte[] readAllBytes() throws IOException {
    long length = length();
    long position = position();
    long size = length - position;

    if (length <= 0 || size <= 0)
        return super.readAllBytes();

    if (size > (long) Integer.MAX_VALUE) {
        String msg =
            String.format("Required array size too large for %s: %d = %d - %d",
                path, size, length, position);
        throw new OutOfMemoryError(msg);
    }

    int capacity = (int)size;
    byte[] buf = new byte[capacity];

    int nread = 0;
    int n;
    for (;;) {
        // read to EOF which may read more or less than initial size, e.g.,
        // file is truncated while we are reading
        while ((n = read(buf, nread, capacity - nread)) > 0)
            nread += n;

        // if last call to read() returned -1, we are done; otherwise,
        // try to read one more byte and if that fails we're done too
        if (n < 0 || (n = read()) < 0)
            break;

        // one more byte was read; need to allocate a larger buffer
        capacity = Math.max(ArraysSupport.newLength(capacity,
                                                    1,         // min growth
                                                    capacity), // pref growth
                            DEFAULT_BUFFER_SIZE);
        buf = Arrays.copyOf(buf, capacity);
        buf[nread++] = (byte)n;
    }
    return (capacity == nread) ? buf : Arrays.copyOf(buf, nread);
}

6. 文件常见操作

这里根据文件的常用操作,使用上面所学的进行实现。主要是以下操作:

  1. 重命名文件;
  2. 文件复制;
  3. 保存网络中的文件;
  4. 删除文件/文件夹;
  5. 文件移动。

需要说明一下,下面只是基于FileIO流的知识来实现的,并不代表只能这样,其实网上有很多方法,不光如此,官方也提供了方便快捷的api

6.1 重命名文件

利用 File 对象的 renameTo 可以轻松实现重命名文件。

boolean fileRename(String path, String newName) {
    File file = new File(path);
    String parentPath = file.getAbsoluteFile().getParent();
    return file.renameTo(new File(parentPath, newName));
}
// 测试
fileRename("test3/b", "a"); // true

6.2 文件复制

这里就需要使用文件的输入输出流了。

/**
 * 文件复制
 * @param sourcePath 被复制的文件
 * @param distPath 复制的目的文件,需要手动指定目的文件名,不会自动跟上一个名称相同
 * @return 复制文件是否成功,失败会抛出异常
 * @throws IOException 文件不存在,权限等不够都会抛出错误
 */
boolean fileCopy(String sourcePath, String distPath) throws IOException {
    FileInputStream fis = new FileInputStream(sourcePath);
    File destParentFile = new File(distPath).getAbsoluteFile().getParentFile();
    // 如果创建文件对应文件夹失败了,那就就直接返回 false
    if (!destParentFile.exists() && !destParentFile.mkdirs()) {
        throw new IOException("Failed to create the target file, probably because there is no permission");
    }
    FileOutputStream fos = new FileOutputStream(distPath);
    fos.write(fis.readAllBytes());
    fis.close();
    fos.close();
    return true;
}

这里我并没有判断 sourcePath 是不是存在等相关检查,因为在执行里面的逻辑会自动抛出错误,所以使用这个函数的时候尽量捕获错误,当然不捕获 java 的 ide 是会报错。光是文件复制太单一了,有时候我们只想复制,不光是文件还是文件夹,下面就展示既支持文件复制同时支持文件复制;当然对于使用的人来说,原地址和目的地址必须是统一的,也就是当时文件的时候两个 path 都得是文件,文件夹的时候同时也是文件夹。

boolean copy(String sourcePath, String distPath) throws IOException {
    File sourceFile = new File(sourcePath);
    // 必须保存源文件或文件夹存在,否则抛出错误
    if (!sourceFile.exists()) {
        throw new IOException("sourcePath file or folder does not exist");
    }
    // 如果是文件就直接执行上面文件复制
    if (sourceFile.isFile()) {
        return fileCopy(sourcePath, distPath);
    }
    // 如果是文件夹,先获取文件夹下的列表
    File[] files = sourceFile.listFiles();
    // 如果获取的时候出现了错误直接抛出错误
    if (files == null) {
        throw new IOException("Failed to get file data");
    }
    // 循环文件中的列表
    for (File file : files) {
        // 新的源文件或文件夹地址
        String tempSourcePath = file.getPath();
        // 新的目的文件或文件夹地址
        String tempDistPath = distPath + "/" + file.getName();
        // 如果是文件,那么直接使用文件复制的方法
        if (file.isFile()) {
            fileCopy(tempSourcePath, tempDistPath);
            continue;
        }
        // 如果是文件夹就再调用这个方法
        copy(tempSourcePath, tempDistPath);
    }
    return true;
}

这个方法当然我觉得是差点意思的,我原本想的是支持事务的,但如果支持事务,那么负责度就上升一个层次;目前如果文件夹有没有权限操作的就会失败,但是正常的就会成功。而且只要出现了错误,后面即便有成功的也会被阻止。

6.3 保存网络中的文件

我就下载谷歌上的一张照片。我这里使用okhttp

boolean downloadGetFile(String url, String distPath) {
    OkHttpClient client = new OkHttpClient();

    // 获取文件的后缀
    String[] urlArr = url.split("/");
    String fileName = urlArr[urlArr.length - 1];
    Request request = new Request.Builder()
            .url(url)
            .build();

    try (Response response = client.newCall(request).execute()) {
        ResponseBody body = response.body();
        byte[] bytes = body.bytes();
        FileOutputStream fos = new FileOutputStream(new File(distPath, fileName));
        fos.write(bytes);
        fos.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return true;
}

// 测试
downloadGetFile("https://www.winrobot360.com/yddoc/static/%E6%89%B9%E9%87%8F%E4%B8%8B%E8%BD%BD%E7%BD%91%E9%A1%B5%E5%9B%BE%E7%89%87%E5%B8%B8%E8%A7%81%E5%9C%BA%E6%99%AF%E5%92%8C%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%886.615ec92e.png","./");

6.4 删除文件/文件夹

删除文件非常简单,Filedelete方法就很合适;下面我写的是既能删除文件也能删除文件夹。

boolean delete(String path) throws IOException {
    File file = new File(path);
    if (!file.exists()) {
        throw new IOException("文件或文件夹不存在");
    }
    if (file.isFile()) {
        return file.delete();
    }
    File[] files = file.listFiles();
    if (files == null) {
        throw new IOException("获取文件列表失败,可能没有权限");
    }
    for (File f: files) {
        String tempPath = f.getPath();
        if (f.isFile()) {
            f.delete();
            continue;
        }
        delete(tempPath);

    }
    // 把所有文件都删除了再删除文件夹
    file.delete();
    return true;
}

6.5 文件移动

我们知道文件名重命名只能重命名,不能像mv指令那样还可以顺便改变路径,下面就实现文件移动,同时也能修改文件的名称。

boolean mv(String sourcePath, String distPath) throws IOException {
    // 创建源文件的 File 对象
    File sourceFile = new File(sourcePath);
    // 创建文件输入流
    FileInputStream fis = new FileInputStream(sourceFile);
	// 先删除源文件
    if (!sourceFile.delete()) {
        throw new Error("没有权限移动文件");
    }
    File destParentFile = new File(distPath).getAbsoluteFile().getParentFile();
    // 如果创建文件对应文件夹失败了,那就就直接返回 false
    if (!destParentFile.exists() && !destParentFile.mkdirs()) {
        throw new IOException("Failed to create the target file, probably because there is no permission");
    }
    FileOutputStream fos = new FileOutputStream(distPath);
    fos.write(fis.readAllBytes());
    fos.close();
    fis.close();
    return true;
}

这里就不实现文件夹的移动了,跟文件复制差不多的思路。

上一篇:Java 中的 File 类