java文件可以边写边读吗_使用IO流复制文件时,为什么write()要放在read()的while循环里,及为啥要边读边写,而不是读完了,再写?...

本博客的内容包含了字节流,缓冲数组的概念。主要是研究为什么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循环里打下了断点:7114ecd77c9c30b09919cfda38b39312.png

结果,在第二次循环时,停下了,紧接着去看照片是否复制好了,的确是由文件,因为执行了一次循环,但是为什么第二次就停止了呢?并且并没有向下执行,而是阻塞在了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();

}

}

好了,博客到现在也就写完了,希望能帮助到遇到此问题的你。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值