java进阶(三)

IO与网络编程

  • 3 输入输出流IO
    • 3.1 基础定义
    • 3.2 IO框架
    • 3.3 读取字节输入流-InputStream
      • 3.3.1 InputStream.read
      • 3.3.2 FileInputStream类说明
    • 3.4读取字符输入流Reader
      • 3.4.1 Reader.read
      • 3.4.2 FileReader类说明
    • 3.5 字节输出流OutputStream
      • 3.5.1 OutputStream.write
      • 3.5.2 FileOutputStream类说明
    • 3.6字符输出流Writer
      • 3.6.1 Writer.write
      • 3.6.2 FileWriter类说明
    • 3.7缓冲流Buffer
      • 3.7.1 字节缓存-BufferedInputStream/BufferedOutputStream
      • 3.7.2 字符缓存-BufferedReader /BufferedWriter
    • 3.8 转换流InputStreamReader/OutputStreamWriter
      • 3.8.1 字节流转字符流
      • 3.7.3 转换流和子类的区别
    • 3.9 对象流ObjectInputStream/ObjectOutputStream
      • 3.9.1对象序列化
      • 3.9.1 对象流转化
    • 3.10 其他流
      • 3.10.1 字节内存流ByteArrayOutputStream/ByteArrayInputStream
      • 3.10.2 字符内存流CharArrayWriter/CharArrayReader
      • 3.10.3 字符串流StringWriter/StringReader
      • 3.10.4合并流-SequenceInputStream
      • 3.10.5 管道流PipedInputStream/PipedOutputStream
    • 3.11 实际应用
      • 3.11.1读写字符文件
      • 3.11.2 复制本地非字符文件
      • 3.11.3 http与IO

3 输入输出流IO

3.1 基础定义

java的输入输出流是比较难懂的地方,什么是java.io?

I/O 是指Input/Output,即输入和输出。
Input指从外部读入数据到内存,例如,把文件从磁盘读取到内存,从网络读取数据到内存等等。
Output指把数据从内存输出到外部,例如,把数据从内存写入到文件,把数据从内存输出到网络等等。

Java程序在执行的时候,是在内存进行的,外部的数据需要读写到内存才能处理;而在内存中的数据是随着程序结束就消失的,有时候我们也需要把数据输出到外部文件。Java中,是通过流 处理IO的,这种处理模式称为 IO流,IO流是一种顺序读写数据的模式。
你可以想象它是一根水管,数据就像水一样, 起点—终点 可互相流动。

java进阶(三)_第1张图片

  • 源头
    从文件、网络、管道,我们称之为目标源

  • 输入/输出
    从目标源读取到内容,我们叫输入,用InputStream或者Reader相关子类来处理。
    从内存输出到目标源,我们称之为输出,用OutputStream或者Writer相关子类来处理

  • 传递介质
    我们想要把目标源转化成Byte或者Char才能传输,Byte用InputStream/OutputStream来操作,Char用Reader/Writer来操作。
    一般情况,视频、音频、图片等,用byte来传递;文字类的用Char来传递方便一些

3.2 IO框架

1.4大框架类

类型 字节流 字符流
输入流 InputStream Reader
输出流 OutputStream Writer

java进阶(三)_第2张图片

3.3 读取字节输入流-InputStream

InputStream 这个抽象类是表示输入字节流的所有类的超类(父类)。

3.3.1 InputStream.read

InputStream 中的三个基本的读方法:

  • int read() :
    读取一个字节数据,并返回读到的数据,如果返回 -1,表示读到了输入流的末尾。
  • int read(byte[] b) :
    将数据读入一个字节数组,同时返回实际读取的字节数。如果返回-1,表示读到了输入流的末尾。
  • int read(byte[] b, int off, int len) :
    将数据读入一个字节数组,同时返回实际读取的字节数。如果返回 -1,表示读到了输入流的末尾。off 指定在数组 b 中存放数据的起始偏移位置;len 指定读取的最大字节数。

InputStream子类

ByteArrayInputStream
FileInputStream
FilterInputStream
PushbackInputStream
DataInputStream
BufferedInputStream
LineNumberInputStream
ObjectInputStream
PipedInputStream
SequenceInputStream
StringBufferInputStream

其中最重要的是FileInputStream、BufferedInputStream

3.3.2 FileInputStream类说明

FileInputStream是文件字节输入流,就是对文件数据以字节的方式来处理,如音乐、视频、图片等。

  //1.一个一个和字节读入
    @Test
    public void t2() throws IOException {
        FileInputStream fis=new FileInputStream("d:\\x.txt");
        int dataByte=0;
        //1.一个一个和字节读入,如果返回-1,表示读取到末尾
        while((dataByte=fis.read())!=-1){
            System.out.print((char)dataByte);
        }
        fis.close();
    }

    //以字节数组读入
    @Test
    public void t3() throws IOException {
        File file=new File("d:\\x.txt");
        InputStream fis=new FileInputStream(file);
        int byte_len=0;
        byte[]bytes=new byte[8];
        //一个字节数组的读出数据,放置到bytes数组李,高效
        while((byte_len=fis.read(bytes))!=-1){
            System.out.print(new String(bytes));
//            for (int i = 0; i < byte_len; i++) {
//                System.out.print((char) bytes[i]);
//            }
        }


    }

    //一次性读入
    @Test
    public void t4() throws IOException {
        File file=new File("d:\\a.txt");
        InputStream fis=new FileInputStream(file);
        //把流里面左右内容转化为字节,jdk1.9以上提供
        byte[]bytes =  fis.readAllBytes();
        System.out.println(new String(bytes));

        //或者用以下方法
        /**
        int iAvail = fis.available(); //总的字节数
        int byte_len=0;
        byte[]bytes2=new byte[iAvail];
        //一个字节数组的读出数据,放置到bytes数组李,高效
        while((byte_len=fis.read(bytes2))!=-1){
            System.out.println(new String(bytes2));
        }
      **/
    }

注意:
英文一般占用1个字节,中文占用3个字节,直接用FileInputStream操作字符形式的文件,容易出现乱码,所以视频、音频、图片这些非字符型的流处理才直接使用FileInputStream

3.4读取字符输入流Reader

我们程序很多时候是操作字符型的输入流,如果用字节inputStream来读取,其是以字节byte形式,容易出现乱码,用Reader操作的是字符流,是字符,不会出现乱码

3.4.1 Reader.read

两则都是通过read方法来读取,一个是直接,一个是字符。

读取方法说明
InputStream int read();//读取一个字节,返回的是字节,本身是一个整数
int read(byte[] bs) //把流内容读取到字节数组bs里
Reader int read();//读取的是一个字符char,返回这个字符对应的整数
int read(char[] chs) //把流的内容读取到一字符数组里

3.4.2 FileReader类说明

我们这个例子是以Reader->InputStreamReader->FileReader为例子

 @Test
    public void t5() throws IOException {
        File file = new File("d:\\x.txt");
        Reader reader=new FileReader(file);
        //一个一个字符读取,注意read返回的是一个整数,用char可以转化为对应的char
        //在java中,char可以用整数表示
        int  ch;
        while((ch=reader.read())!=-1){
            System.out.print((char)ch);
        }
        reader.close();

    }

    @Test
    public void t6() throws IOException {
        File file = new File("d:\\x.txt");
        Reader reader=new FileReader(file);
        //读取一个字符数组,高效
        int  len=-1;
        char[] ch=new char[10];
         //这里字符数组的大写可以任意定义,也可以定义全部大小
        //ch=new char[(int)file.length()];  //这个写法是long->int,如果文字内容太大,可能造成丢失
        while((len=reader.read(ch))!=-1){
            System.out.print(new String(ch));
        }

        //或者这样写  
        while((len=reader.read(ch,0,10))!=-1){
            System.out.print(new String(ch));
        }
        reader.close();

    }

3.5 字节输出流OutputStream

3.5.1 OutputStream.write

OutputStream最重要的方法就是write
OutputStream源码

 /**
  b就是字节值
  如果读完流后返回-1
     */
public abstract void write(int b) throws IOException;

    /**
   字节数组
   如果读完流后返回-1
     */
    public void write(byte b[]) throws IOException {
        write(b, 0, b.length);
    }

    /**
     字节数组
     如果读完流后返回-1
     */
    public void write(byte b[], int off, int len) throws IOException {
        Objects.checkFromIndexSize(off, len, b.length);
        // len == 0 condition implicitly handled by loop bounds
        for (int i = 0 ; i < len ; i++) {
            write(b[off + i]);
        }
    }

3.5.2 FileOutputStream类说明

OutputStream->FileOutputStream,FileOutputStream是处理文件流的类

 @Test
    public void t7() throws IOException {
        File file = new File("d:\\jj.txt");
        //把字符串写入
        OutputStream os=new FileOutputStream(file);
        String str="我热你温\n你个蛤蟆皮";
        os.write(str.getBytes());
        os.close();


    }

    //文件复制

    @Test
    public void t8() throws IOException {
        File file = new File("d:\\a.txt");
        File target = new File("d:\\jb.txt");
        InputStream is=new FileInputStream(file);
        OutputStream os=new FileOutputStream(target);
        int len=-1;
        byte[] bs=new byte[10];
        while((len=is.read(bs))!=-1){
            os.write(bs);
        }
        System.out.println("copy ok");
        is.close();
        os.close();


    }

    @Test
    public void t9() throws IOException {
        File file = new File("d:\\a.txt");
        File target = new File("d:\\jb.txt");
        InputStream is=new FileInputStream(file);
        OutputStream os=new FileOutputStream(target);
        int c=-1;

        while((c=is.read())!=-1){
            os.write(c);
        }
        System.out.println("copy ok");
        is.close();
        os.close();
    }

3.6字符输出流Writer

OutputStream一般用于视频、音频、图片等非字符类的资源流,如果是文字类型,用Writer最方便

3.6.1 Writer.write

和OutputStream一致,其最重要的方法就是write方法
Writer源码:

/**
    输入一个char字符对应的整数
 */
public void write(int c) throws IOException {
        synchronized (lock) {
            if (writeBuffer == null){
                writeBuffer = new char[WRITE_BUFFER_SIZE];
            }
            writeBuffer[0] = (char) c;
            write(writeBuffer, 0, 1);
        }
    }

    /**
     输入char数组
     */
    public void write(char cbuf[]) throws IOException {
        write(cbuf, 0, cbuf.length);
    }

    /**
  字符串
     */
    public void write(String str) throws IOException {
        write(str, 0, str.length());
    }

    /**
  字符串
     */
    public void write(String str, int off, int len) throws IOException {
        synchronized (lock) {
        .....
        }
    }

3.6.2 FileWriter类说明

代码说明

@Test
    public void t10() throws IOException {
        File file = new File("d:\\x.txt");
        String str="宇宙第一胎神,四川蛤蟆皮";
        Writer writer=new FileWriter(file);
        writer.write(str);
        writer.close();
    }

    @Test
    public void t11() throws IOException {

        File file = new File("d:\\a.txt");
        File target = new File("d:\\jb2.txt");
        Reader rd=new FileReader(file);
        Writer wt=new FileWriter(target);
        int c=-1;
        //读取字符
        while((c=rd.read())!=-1){
            wt.write(c);
        }
        System.out.println("copy ok");
        rd.close();
        wt.close();
    }

    @Test
    public void t12() throws IOException {

        File file = new File("d:\\a.txt");
        File target = new File("d:\\jb3.txt");
        Reader rd=new FileReader(file);
        Writer wt=new FileWriter(target);
        int len=-1;
        char[] cs=new char[20];
        //读取字符
        while((len=rd.read(cs))!=-1){
            wt.write(cs);
        }
        System.out.println("copy ok");
        rd.close();
        wt.close();
    }

3.7缓冲流Buffer

说明是包装流

①、包装流隐藏了底层节点流的差异,并对外提供了更方便的输入\输出功能,让我们只关心这个高级流的操作
②、使用包装流包装了节点流,程序直接操作包装流,而底层还是节点流和IO设备操作
③、关闭包装流的时候,只需要关闭包装流即可

缓冲流:是一个包装流,目的是缓存作用,加快读取和写入数据的速度。

字节缓冲流:BufferedInputStream、BufferedOutputStream

字符缓冲流:BufferedReader、BufferedWriter
java进阶(三)_第3张图片

说明:

我们在将字符输入输出流、字节输入输出流的时候,读取操作,通常都会定义一个字节或字符数组,将读取/写入的数据先存放到这个数组里面,然后在取数组里面的数据。这比我们一个一个的读取/写入数据要快很多,而这也就缓冲流的由来。只不过缓冲流里面定义了一个 数组用来存储我们读取/写入的数据,当内部定义的数组满了(注意:我们操作的时候外部还是会定义一个小的数组,小数组放入到内部数组中),就会进行下一步操作。

java进阶(三)_第4张图片
源码查看:
java进阶(三)_第5张图片
再次说明,8192是BufferedXXX内部读取一个缓存,外部依然要定义一个数组去读取,只是读取是缓存里的数据,从而提升了读写效率。
缓存流是一个装饰模式,把对应的流放入BufferedXXX里面(通过构造函数),其读写方法和对应的Inputout等一模一样。

3.7.1 字节缓存-BufferedInputStream/BufferedOutputStream

 File file = new File("d:\\a.txt");
 File target = new File("d:\\jb3.txt");
 
 BufferedInputStream bis=new BufferedInputStream(new FileInputStream(file));
 BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(target));
 
 BufferedReader br=new BufferedReader(new FileReader(file));
 BufferedWriter bos=new BufferedWriter(new FileWriter(target));

测试代码

    @Test
    public void t13() throws IOException {
        File file = new File("d:\\a.txt");
        File target = new File("d:\\jb3.txt");
        BufferedInputStream bis=new BufferedInputStream(new FileInputStream(file));
        BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(target));

        int len=-1;
        byte[] bs=new byte[8];
        while((len=bis.read(bs))!=-1){
            bos.write(bs);
        }
        System.out.println("copy ok");
        bis.close();
        bos.close();


    }


3.7.2 字符缓存-BufferedReader /BufferedWriter

还提供了BufferedReader…readLine()逐行读取读取方法;BufferedWriter.write(str)写入字符串

    @Test
    public void t14() throws IOException {
        File file = new File("d:\\a.txt");
        File target = new File("d:\\jb3.txt");
        BufferedReader br=new BufferedReader(new FileReader(file));
        BufferedWriter bw=new BufferedWriter(new FileWriter(target));
        int len=-1;
        char[] chs=new char[10];
        while ((len=br.read(chs))!=-1){
            bw.write(chs);
        }
	 //注意BufferedReader还提供了readLine()读取一行的快捷功能,下面写法也是一样的
        /**
        String line;
        // 逐行读取
        while ((line = br.readLine()) != null) {
            bw.write(line);
        }
         **/
        br.close();
        bw.close();
        System.out.println("copy ok");
    }

使用方式基本和FileInputStream一致。

BufferedInputStream有一个内部缓冲区数组,一次性读取较多的字节缓存起来,默认读取defaultBufferSize = 8192,作用于读文件时可以提高性能。

public XssRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        this.request = request;
        StringBuilder stringBuilder = new StringBuilder();
        BufferedReader bufferedReader = null;
        try {
            InputStream inputStream = request.getInputStream();
            if (inputStream != null) {
                // 此处需要将编码格式设置为UTF_8,解决 InputStream 流读取时的中文乱码问题
                bufferedReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
                char[] charBuffer = new char[128];
                int bytesRead = -1;
                while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
                    stringBuilder.append(charBuffer, 0, bytesRead);
                }
            } else {
                stringBuilder.append("");
            }
        } catch (IOException ex) {
            throw ex;
        } finally {
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (IOException ex) {
                    throw ex;
                }
            }
        }

        System.out.println("stringBuilder: " + stringBuilder.toString());
}

3.8 转换流InputStreamReader/OutputStreamWriter

3.8.1 字节流转字符流

InputStreamReader:把字节输入流转换为字符输入流
OutputStreamWriter:把字节输出流转换为字符输出流

Reader->InputStreamReader:把字节输入流转换为字符输入流
Writer->OutputStreamWriter:把字节输出流转换为字符输出流

都是通过构造函数来实例化

InputStreamReader isr=new InputStreamReader(new InputStream);
OutputStreamWriter osw=new OutputStreamWriter(new OutputStream );

注意:他们都是字符流Reader、Writer的子类
java进阶(三)_第6张图片
转换流就是把字节流转化成字符流,可以用char来读写

测试代码

 @Test
    public void t15() throws IOException {
        File file = new File("d:\\a.txt");
        File target = new File("d:\\jb3.txt");
        InputStream is=new FileInputStream(file);
        //转化成字符输入流
        InputStreamReader isr=new InputStreamReader(is);

        OutputStream os=new FileOutputStream(target);
        //转化成字符输出流
        OutputStreamWriter osw=new OutputStreamWriter(os);

        int len=-1;
        char[] buffer=new char[10];
        while((len=isr.read(buffer))!=-1){
            osw.write(buffer);
        }
        System.out.println("copy ok");
        //is.close();//不需要
        isr.close();
       // os.close();  //不需要
        osw.close();

    }

3.7.3 转换流和子类的区别

我们看
OutputStreamWriter:
|–FileWriter:
InputStreamReader:
|–FileReader;
java进阶(三)_第7张图片
父类和子类的功能有什么区别呢?

OutputStreamWriter和InputStreamReader是字符和字节的桥梁:
也可以称之为字符转换流。字符转换流原理:字节流+编码表

FileWriter和FileReader:

作为子类,仅作为操作字符文件的便捷类存在。当操作的字符文件,使用的是默认编码表时可以不用父类,而直接用子类就完成操作了,简化了代码。

InputStreamReader isr = new InputStreamReader(new FileInputStream(“a.txt”));//默认字符集。
InputStreamReader isr = new InputStreamReader(new FileInputStream(“a.txt”),StandardCharsets.UTF_8);//指定字符集。
FileReader fr = new FileReader(“a.txt”);

这三句代码的功能是一样的,其中第三句最为便捷。

注意:一旦要指定其他编码时,绝对不能用子类,必须使用字符转换流。
什么时候用子类呢?
条件:
1、操作的是文件。2、使用默认编码。
总结:
字节—>字符 : 看不懂的—>看的懂的。 需要读。输入流。 InputStreamReader

3.9 对象流ObjectInputStream/ObjectOutputStream

3.9.1对象序列化

1.什么是序列化与反序列化?

序列化:指把堆内存中的 Java 对象数据,通过某种方式把对象存储到磁盘文件中或者传递给其他网络节点(在网络上传输)。这个过程称为序列化。通俗来说就是将数据结构或对象转换成二进制串的过程

反序列化:把磁盘文件中的对象数据或者把网络节点上的对象数据,恢复成Java对象模型的过程。也就是将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程

2.为什么要做序列化?

①、在分布式系统中,此时需要把对象在网络上传输,就得把对象数据转换为二进制形式,需要共享的数据的 JavaBean 对象,都得做序列化。

②、服务器钝化:如果服务器发现某些对象好久没活动了,那么服务器就会把这些内存中的对象持久化在本地磁盘文件中(Java对象转换为二进制文件);如果服务器发现某些对象需要活动时,先去内存中寻找,找不到再去磁盘文件中反序列化我们的对象数据,恢复成 Java 对象。这样能节省服务器内存。

3.Java 怎么进行序列化?

①、需要做序列化的对象的类,必须实现序列化接口:Java.lang.Serializable 接口(这是一个标志接口,没有任何抽象方法),Java 中大多数类都实现了该接口,比如:String,Integer

②、底层会判断,如果当前对象是 Serializable 的实例,才允许做序列化,Java对象 instanceof Serializable 来判断。

③、在 Java 中使用对象流来完成序列化和反序列化

ObjectOutputStream:通过 writeObject()方法做序列化操作

ObjectInputStream:通过 readObject() 方法做反序列化操作

java进阶(三)_第8张图片

3.9.1 对象流转化

vo对象

@Data
@AllArgsConstructor
public class TestVO implements Serializable {

    private static final long serialVersionUID = 3275714157646111915L;
    private Integer id;
    private String name;
}

测试效果

@Test
    public void t16() throws IOException, ClassNotFoundException {
      //1.对象序列化:通过ObjectOutputStream对象把序列化对象写入磁盘文件
        File file = new File("d:\\o.txt");

        OutputStream os=new FileOutputStream(file);
        ObjectOutputStream oos=new ObjectOutputStream(os);
        TestVO vo=new TestVO(1, "蒋增奎");
        oos.writeObject(vo);
        System.out.println("写入成功");
        oos.close();

        //2.对象反序列化,通过ObjectInputStream从磁盘文件中读取到内存
        InputStream is=new FileInputStream(file);
        ObjectInputStream ois=new ObjectInputStream(is);
        TestVO vo1=(TestVO)ois.readObject();
        System.out.println(vo1);
        System.out.println("读取成功");
        ois.close();
    }

注意:
(1)如果没有继承java.io.Serializable接口,序列化报异常
java进阶(三)_第9张图片
(2)可能出现这种情况,我们序列化后,我们的VO对象改变了,如增加了一个字段,如果vo对象里没有
serialVersionUID,会报异常,其实这里serialVersionUID起一个版本作用

private static final long serialVersionUID = 3275714157646111915L;

3.10 其他流

其他几种流,我们用得很少,这里简单介绍一下

  • 内存流(数组流):
    (1)字节内存流:ByteArrayOutputStream 、ByteArrayInputStream
    (2)字符内存流:CharArrayReader、CharArrayWriter
    (3)字符串流:StringReader,StringWriter(把数据临时存储到字符串中)
    (4)合并流:SequenceInputStream 把多个输入流合并为一个流,也叫顺序流,因为在读取的时候是先读第一个,读完了在读下面一个流。
    (5)管道流: PipedInputStream和 PipedOutputStream

3.10.1 字节内存流ByteArrayOutputStream/ByteArrayInputStream

//字节数组输出流:程序---》内存
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        //将数据写入到内存中
        bos.write("ABCD".getBytes());
        //创建一个新分配的字节数组。 其大小是此输出流的当前大小,缓冲区的有效内容已被复制到其中。
        byte[] temp = bos.toByteArray();
        System.out.println(new String(temp,0,temp.length));
         
        byte[] buffer = new byte[10];
        ///字节数组输入流:内存---》程序
        ByteArrayInputStream bis = new ByteArrayInputStream(temp);
        int len = -1;
        while((len=bis.read(buffer))!=-1){
            System.out.println(new String(buffer,0,len));
        }
         
        //这里不写也没事,因为源码中的 close()是一个空的方法体
        bos.close();
        bis.close();

3.10.2 字符内存流CharArrayWriter/CharArrayReader

//字符数组输出流
        CharArrayWriter caw = new CharArrayWriter();
        caw.write("ABCD");
        //返回内存数据的副本
        char[] temp = caw.toCharArray();
        System.out.println(new String(temp));
         
        //字符数组输入流
        CharArrayReader car = new CharArrayReader(temp);
        char[] buffer = new char[10];
        int len = -1;
        while((len=car.read(buffer))!=-1){
            System.out.println(new String(buffer,0,len));
        }

3.10.3 字符串流StringWriter/StringReader

		//字符串输出流,底层采用 StringBuffer 进行拼接
        StringWriter sw = new StringWriter();
        sw.write("ABCD");
        sw.write("帅锅");
        System.out.println(sw.toString());//ABCD帅锅
 
        //字符串输入流
        StringReader sr = new StringReader(sw.toString());
        char[] buffer = new char[10];
        int len = -1;
        while((len=sr.read(buffer))!=-1){
            System.out.println(new String(buffer,0,len));//ABCD帅锅
        }

3.10.4合并流-SequenceInputStream

//定义字节输入合并流
        SequenceInputStream seinput = new SequenceInputStream(
                new FileInputStream("io/a.txt"), new FileInputStream("io/b.txt"));
        byte[] buffer = new byte[10];
        int len = -1;
        while((len=seinput.read(buffer))!=-1){
            System.out.println(new String(buffer,0,len));
        }
         
        seinput.close();

3.10.5 管道流PipedInputStream/PipedOutputStream

管道主要用来实现同一个虚拟机中的两个线程进行交流。因此,一个管道既可以作为数据源媒介也可作为目标媒介。
需要注意的是java中的管道和Unix/Linux中的管道含义并不一样,在Unix/Linux中管道可以作为两个位于不同空间进程通信的媒介,而在java中,管道只能为同一个JVM进程中的不同线程进行通信。和管道相关的IO类为: PipedInputStream和 PipedOutputStream,下面我们来看一个例子:

public class PipeExample {
   public static void main(String[] args) throws IOException {
          final PipedOutputStream output = new PipedOutputStream();
          final PipedInputStream  input  = new PipedInputStream(output);
          Thread thread1 = new Thread( new Runnable() {
              @Override
              public void run() {
                  try {
                      output.write( "Hello world, pipe!".getBytes());
                  } catch (IOException e) {
                  }
              }
          });
          Thread thread2 = new Thread( new Runnable() {
              @Override
              public void run() {
                  try {
                      int data = input.read();
                      while( data != -1){
                          System. out.print(( char) data);
                          data = input.read();
                      }
                  } catch (IOException e) {
                  } finally{
                     try {
                                       input.close();
                                } catch (IOException e) {
                                       e.printStackTrace();
                                }
                  }
              }
          });
          thread1.start();
          thread2.start();
      }
}

3.11 实际应用

上面基本比较全面的介绍了IO框架及用法,我们下面介绍集中典型的业务场景

3.11.1读写字符文件

磁盘上的字符文件,如a.txt,Hello.java,我们最佳采用BufferedReader/BufferWriter来实现,上面有很多例子,不再举例

3.11.2 复制本地非字符文件

复制非字符文件如视频,图片,音频等,用BufferedInputStream/BufferedOutputStream

 @Test
    public void t17() throws IOException{
        //复制非字符文件如视频、音频、图片、压缩文件等
        File file = new File("d:\\格式工厂V3.3.3.0 绿色版.zip");
        InputStream is=new FileInputStream(file);
        BufferedInputStream bis=new BufferedInputStream(is);

        File target = new File("d:\\格式工厂V3.3.3.0.zip");
        OutputStream os=new FileOutputStream(target);
        BufferedOutputStream bos=new BufferedOutputStream(os);
        int len=-1;
        byte[] buffer=new byte[10];
        while ((len=bis.read(buffer))!=-1){
            bos.write(buffer,0,len);
        }

        bis.close();
        bos.close();
        System.out.println("copy ok");

    }

3.11.3 http与IO

基于http通信的web服务,客户端和服务器端都存在数据交换,都可以转化成InputStream或OutputStream流。

  • InputStream与URL

URL url = new URL(httpUrl);//创建URL
InputStream inputStream=url.openStream();
//通过InputStream可以非常方便的解析除url产生的数据

  • Request和InputStream

ServletInputStream sip=HttpServletRequest.getInputStream();
//ServletInputStream 就是InputStream的子类

  • Response和OutputStream

ServletOutputStream d= HttpServletResponse.getOutputStream();
ServletOutputStream 就是OutputStream的子类
PrintWriter out = response.getWriter();
//PrintWriter是Writer的子类

实际案例

1.解析一个httpURL文本

如果第三方服务器发布一个http服务,如html,json,xml我们可以用流的方式读取
inputStream = url.openStream();获得输入流

@Test
    public void t18() throws Exception {
        System.out.parHttpUrlTxt(doGet("https://www.baidu.com/"));
    }

//inputStream = url.openStream();获得输入流
  public static String parHttpUrlTxt(String httpUrl) throws Exception {
        InputStream inputStream = null;
        StringBuffer result = new StringBuffer();
        BufferedReader bufferedReader = null;
        //初始化URL,传入想访问的地址
        URL url = new URL(httpUrl);
        //获得一个
        inputStream = url.openStream();
        String inputLine;
        if (inputStream != null) {
            bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
            while ((inputLine = bufferedReader.readLine()) != null) {

                result = result.append(inputLine+"\n");
            }
        }

        bufferedReader.close();

        return result.toString();
}

2.下载网络资源

 @Test
    public void t19() throws Exception {
        //下载网络图片
        String httpUrl = "https://img-blog.csdnimg.cn/direct/0c276443e93143fbb98c23d6551fb0aa.png";
        URL url = new URL(httpUrl);


        InputStream inputStream = url.openStream();
        BufferedInputStream bis = new BufferedInputStream(inputStream);
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("d:\\sex.png"));
        int len = -1;
        byte[] buff = new byte[10];
        while ((len = bis.read(buff)) != -1) {
            bos.write(buff, 0, len);//用 bos.write(buff)要出错,如果是图片
        }
        System.out.println("down is ok");

        bos.close();
        bis.close();


    }
  1. servlet自动生成一张图片

设置头

resp.setContentType(“image/jpeg”);

获得输出流

ServletOutputStream sos = response.getOutputStream();
ServletOutputStream是OutputStream的子类

/**
 * Desc: ImageServlet
 * Author: Xu He
 * created: 2021/9/7 10:34
 */
 
@WebServlet(urlPatterns = {"/imageServlet"})
public class ImageServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
 
    }
 
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.创建字节输入流,关联读取的文件
        // 1.1 获取文件的绝对路径
        String realPath = getServletContext().getRealPath("/img/lyf.jpeg");
        // 1.2 创建字节输出流对象
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(realPath));
 
        //2.设置响应头支持的类型  应用支持的类型为字节流
        /*
            Content-Type 消息头名称   支持的类型
            image/jpeg   消息头参数  应用类型为图片
         */
        // resp.setHeader(" Content-Type", "image/jpeg");
        // 当 header 的 key 是 Content-Type, 可以使用 resp.setContentType 方法
        resp.setContentType("image/jpeg");
 
        //3.获取字节输出流对象
        ServletOutputStream sos = resp.getOutputStream();
 
        //4.循环读写文件
        byte[] arr = new byte[1024];
        int len;
        while((len = bis.read(arr)) != -1) {
            sos.write(arr,0,len);
        }
 
        //6.释放资源
        bis.close();
        sos.close();
    }
}

  1. servlet下载资源连接

ContentType格式

response.setContentType(“APPLICATION/OCTET-STREAM”);
response.setHeader(“Content-Disposition”, “attachment; filename=”" + filename + “”");

获得输出类PrintWriter

PrintWriter out = response.getWriter();
PrintWriter是Writer的子类

public class DownloadServlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");
        request.setCharacterEncoding("UTF-8");
        PrintWriter out = response.getWriter();
        String filepath = request.getSession().getServletContext().getRealPath("");
        String filename = "home.jsp";
        response.setContentType("APPLICATION/OCTET-STREAM");
        response.setHeader("Content-Disposition", "attachment; filename=\"" + filename + "\"");

        FileInputStream fileInputStream = new FileInputStream(filepath + filename);

        int i = 0;
        while ((i = fileInputStream.read()) != -1) {
            out.write(i);
        }
        fileInputStream.close();
        out.close();
    }

}
//更多请阅读:https://www.yiibai.com/servlet/example-of-downloading-file-from-the-server-in-servlet.html


你可能感兴趣的:(java,python,开发语言)