关于java:使用IO流复制文件时为什么write要放在read的while循环里及为啥要边读边写而不是读完了再写

151次阅读

共计 4453 个字符,预计需要花费 12 分钟才能阅读完成。

本博客的内容蕴含了字节流,缓冲数组的概念。次要是钻研 为什么 write(bytes)要放在 read(bytes)的 while 循环里,及为啥要一边读,一遍写,而不是读完了(输出),再写(输入)。

明天学习到 TCP 时, 应用 Client 向 Server 传入数据,其实这就是一种变相的 复制文件 ,只是输入的目的地是服务器罢了。
上面是服务端的代码,没有任何问题。

public class Server {public static void main(String[] args) throws IOException {
        //1. 创立 serverSocket 对象,传入端口号
        ServerSocket serverSocket = new ServerSocket(8888);
        //2. 用 serverSocket 接管,客户端申请的 socket 对象
        Socket socket = serverSocket.accept();
        //3. 应用 socket 对象来获取网络输出流对象,用来读取客户端输入的数据
        InputStream is = socket.getInputStream();
        // 如果文件夹不存在,则创立一个
        File file = new File("F:\\FileUpdate");
        if (!file.exists()){file.mkdirs();
        }
        //4. 创立本地输入流对象
        FileOutputStream fos = new FileOutputStream(file+"\\dog.jpg");
        // 保留客户端输入的数据
        byte[] bytes = new byte[1024];
        int len = 0 ;
        // 网络输出流,读取客户端发送的数据,并复制到本地输入流对象指定的目的地
        while((len = is.read(bytes))!=-1){System.out.println("保留中");
            fos.write(bytes);
        }
        System.out.println("wocap");
        //5. 利用 socket 对象,获取网络输入流对象
        OutputStream os = socket.getOutputStream();
        //6. 向客户端发送数据
        if (bytes!=null){os.write("你好,你发送的数据曾经保留胜利".getBytes());
        }else {os.write("操作异样,请再次传输数据".getBytes());
        }
        //7. 开释资源
        fos.close();
        serverSocket.close();
        socket.close();}
}

在复制文件时,产生了点小插曲,就是服务器下载不下来文件,所以我就调试,在服务器程序中的 读取文件的 while 循环 里打下了断点:
后果,在第二次循环时,停下了,紧接着去看照片是否复制好了,确实是由文件,因为执行了一次循环,然而为什么第二次就进行了呢?并且并没有向下执行,而是 阻塞在了 while 循环第一句 ,我想应该是 is.read(bytes) 的问题,于是查看 API, 以下是原文:

public int read(byte[] b)

     throws IOException 从输出流中读取肯定数量的字节,并将其 ` 存储在缓冲区数组 b ` 中。以整数模式返回理论读取的字节数。` 在输出数据可用、检测到文件开端或者抛出异样前,此办法始终阻塞。`

如果 b 的长度为 0,则不读取任何字节并返回 0;否则,尝试读取至多一个字节。如果因为流位于文件开端而没有可用的字节,则返回值 -1;否则,至多读取一个字节并将其存储在 b 中。

将读取的第一个字节存储在元素 b[0] 中,下一个存储在 b[1] 中 ,顺次类推。读取的字节数最多等于 b 的长度。设 k 为理论读取的字节数;这些字节将存储在 b[0] 到 b[k-1] 的元素中, 不影响 b[k] 到 b[b.length-1] 的元素

通过 API 原文咱们能够发现,阻塞在 while 循环第一句的起因:是没有检测到文件开端及 -1, 所以程序始终被阻塞了,而没有向下执行。

那么问题来了,为啥没有咱们的文件没有数据了呢,起因只有一个,那就是客户端没有把文件的字节全副输入,所以回头看, 发现咱们的 Client 里呈现了谬误:

while((len = fis.read(bytes))!=-1){System.out.println("传输中");
        }
        os.write(bytes);

对,没错就是明天题目,应用字节流复制文件时,为什么 write(bytes)要放在 read(bytes)的 while 循环里 , 通过 API 的介绍咱们晓得了咱们形参 bytes 是一个缓冲数组,所谓缓冲数组,就是临时放进,然而在 再次调用会从新赋值

于是为了一探到底我就去看了源码,然而发现他的底层是一个 native 申明的函数,看不见,native 申明的函数是其余语言编写的,如 C ++,然而我想到了一个办法,怎么钻研 缓冲数组是否会被从新赋值.

就是通过,每次输入缓冲数组的第一个字节:代码如下:

public class copyDemo1 {public static void main(String[] args) throws IOException {FileInputStream fis = new FileInputStream("C:\\Users\\asusc\\Pictures\\Saved Pictures\\dog.jpg");
        FileOutputStream fos = new FileOutputStream("F.jpg");
        byte[] bytes = new byte[1024];
        int readLen= 0;
        int i =0;
        while((readLen= fis.read(bytes))!=-1){System.out.println("缓冲数组第一个字节"+bytes[0]);
        }
        fos.write(bytes,0,readLen);

        fos.close();
        fis.close();}
}

输入后果:

缓冲数组第一个字节 -1
缓冲数组第一个字节 -91
缓冲数组第一个字节 7
缓冲数组第一个字节 117
缓冲数组第一个字节 -86
缓冲数组第一个字节 -55
缓冲数组第一个字节 -110

不言而喻,咱们的思路是对的,缓冲数组,确实会在每次被调用时,被从新赋值

所以也就阐明了为啥 write(bytes) 要放在 read(bytes)的 while 循环里,及为啥要一边读,一遍写。因为缓冲数组,会被从新赋值,如果把 write()放在 read()办法的 while 循环里面,会造成数组越界的异样。下面的例子也会抛出异样,因为 write()写数据时,写完那 1024 个字节的数组,就没了, 缓冲数组为空。

上面是 客户端向服务器传入数据 的正确代码:

客户端

package Net.FileUpdate;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class Client {public static void main(String[] args) throws IOException {
        //1. 创立本地文件输出流,读取本地的文件
        FileInputStream fis = new FileInputStream("C:\\Users\\asusc\\Pictures\\Saved Pictures\\dog.jpg");
        //2. 创立 socket 对象,传入 ip 和端口号
        Socket socket = new Socket("localhost",8888);
        // 装被读取文件的字节数组
        byte[] bytes = new byte[1024];
        int len = 0;
        
        //3. 获取网络输入流的对象,向服务端写入数据
        OutputStream os = socket.getOutputStream();

        while((len = fis.read(bytes))!=-1){System.out.println("传输中");
            os.write(bytes);
        }

        //4. 获取网络输出流的对象,用于读取服务器输出的数据
        InputStream is = socket.getInputStream();
        byte[] bytes1 = new byte[1024];
        int len1 = is.read(bytes);
        System.out.println(new String(bytes1,0,len));
        //5. 开释资源
        fis.close();
        socket.close();}
}

服务端

package Net.FileUpdate;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {public static void main(String[] args) throws IOException {
        //1. 创立 serverSocket 对象,传入端口号
        ServerSocket serverSocket = new ServerSocket(8888);
        //2. 用 serverSocket 接管,客户端申请的 socket 对象
        Socket socket = serverSocket.accept();
        //3. 应用 socket 对象来获取网络输出流对象,用来读取客户端输入的数据
        InputStream is = socket.getInputStream();
        // 如果文件夹不存在,则创立一个
        File file = new File("F:\\FileUpdate");
        if (!file.exists()){file.mkdirs();
        }
        //4. 创立本地输入流对象
        FileOutputStream fos = new FileOutputStream(file+"\\dog.jpg");
        // 保留客户端输入的数据
        byte[] bytes = new byte[1024];
        int len = 0 ;
        // 网络输出流,读取客户端发送的数据,并复制到本地输入流对象指定的目的地
        while((len = is.read(bytes))!=-1){System.out.println("保留中");
            fos.write(bytes);
        }
        System.out.println("wocap");
        //5. 利用 socket 对象,获取网络输入流对象
        OutputStream os = socket.getOutputStream();
        //6. 向客户端发送数据
        if (bytes!=null){os.write("你好,你发送的数据曾经保留胜利".getBytes());
        }else {os.write("操作异样,请再次传输数据".getBytes());
        }
        //7. 开释资源
        fos.close();
        serverSocket.close();
        socket.close();}
}

好了,博客到当初也就写完了,心愿能帮忙到遇到此问题的你。

正文完
 0