美文网首页
Node.Js与Java的IO流,及底层会发生的故事。

Node.Js与Java的IO流,及底层会发生的故事。

作者: LeeYaMaster | 来源:发表于2019-05-06 23:42 被阅读0次

    首先,我们来看Node.Js读取文件的操作

    var http = require('http');
    var fs = require('fs');
    var path = require('path');
    //文件读取
    var server = http.createServer(function (req, res) {
        var fileName = path.resolve(__dirname, 'input.txt');
        fs.readFile(fileName, function (err, data) {
            res.end(data);
        });
    });
    server.listen(8888); 
    

    这样就可以读取到同级目录下的input.txt文件了,仔细看代码,也没问题,可是当遇到文件比较大,有一个G,两个G呢?代码就不能这样写了,这个时候,就应该用stream流了:

    var http = require('http');
    var fs = require('fs');
    var path = require('path');
    //以流的方式文件读取。
    
    var http = require('http');
    var fs = require('fs');
    var path = require('path');
    
    var server = http.createServer(function (req, res) {
        var fileName = path.resolve(__dirname, 'data.txt');
        var stream = fs.createReadStream(fileName);  //创建一个读的流
        stream.pipe(res); // 将 res 作为 stream 的 接受者
    });
    server.listen(8888); 
    

    input.txt照样可以输出到网页。于是,我们现在详细讲一下这个流:

    图片摘自菜鸟教程

    为什么要用流呢?我们把要输入的文件,比作一个水桶,我们要抱起他,要用很大力气,这里的力气指的是
    计算机硬件的性能, CPU 的运算,内存的存储,硬盘和网络的读写速度。但是我们没必要抱起它,弄一个水管就好了,这就是stream的作用,同理,我们看视频,也是同一个道理,不可能全部加载完再让你看。

    知道了流的原理,那我们写一个文件拷贝吧:

    Node版本

    let fileName1 = path.resolve(__dirname,'input.txt');
    let fileName2 = path.resolve(__dirname,'input1.txt');
    let readStream = fs.createReadStream(fileName1);
    let writeStream = fs.createWriteStream(fileName2);
    readStream.pipe(writeStream);
    readStream.on("end",function(){
        console.log("拷贝完成");
    })
    
    

    Java版本,字节流(适合二进制文件,图片,视频,音频 ):

    public static void demo() throws IOException {
        //1.获取目标路径
        //第一种方法:通过字符串
        //String srcPath = "图片路径";
        //String destPath = "图片路径";
        //第二种方法:通过文件类
        File srcPath = new File("图片路径");
        File destPath = new File("图片路径");
        //2.创建通道,依次 打开输入流,输出流
        FileInputStream fis = new FileInputStream(srcPath);
        FileOutputStream fos = new FileOutputStream(destPath);
        byte[] bt = new byte[1024];
        //3.读取和写入信息(边读取边写入)
        while (fis.read(bt) != -1) { //读取
            fos.write(bt); //写入
        }
        //4.依次 关闭流(先开后关,后开先关)
        fos.close();//先关输出流
        fis.close();//后关输入流
    }
    
    

    Java版本,字符流:(只能读文本之类的文件,但字符流提供缓冲(Buffer)功能可以提高读取效率。)

    public static void demo() throws IOException {
        //获取目标路径
        File srcPath = new File("图片路径");
        File destPath = new File("图片路径");
        //创建通道,依次 打开输入流,输出流
        FileReader fis = new FileReader(srcPath);
        FileWriter fos = new FileWriter(destPath);
        char[] ch = new char[1024];
        int length = 0;
        // 读取和写入信息(边读取边写入)
        while ((length = fis.read(ch)) != -1) { //读取
            fos.write(ch, 0, length); //写入
            fos.flush();
        }
        // 依次 关闭流(先开后关,后开先关)
        fos.close();//先关输出流
        fis.close();//后关输入流
    }
    
    菜鸟教程IO示意图

    IO流就讲这么多了,其实就是数据不停地搬入搬出缓冲区Buffer而已,上面提到了Buffer缓冲区,以及读取二进制文件,拷贝功能,这就不得不扯到一个底层的东西,那就是操作系统。上面的代码,Java工资5k可以写出来,工资5w,也可以写出来,还有可能写的一样,但是Java5k的人,只会写出这段代码,而5w的人,不仅可以写出这段代码,还理解操作系统的底层原理,还可以自己用底层,自己实现一个IO类,这就是区别。下面就是操作系统的讲解啦,如果想要工资1W+,这东西是必须知道的!

    操作系统

    关于操作系统,最近在准备软件设计师的考试,以及考研,也会考到操作系统的知识,下面有些理论是抄自书上的。

    五大IO模型

    IO是什么?,I就是从硬盘到内存,O就是从内存到硬盘。关于五大IO模型,我用白话文讲述下,因为官方文档,挺绕的。
    (1)阻塞:
    我从硬盘读取数据,然后程序一直等,数据读完后,继续操作。这种方式是最简单的,程序多了,自然就不行了。
    (2)非阻塞
    我从硬盘读取数据,然后程序跑,我去做其他事,等跑完了,再叫我做事。
    (3)IO多路复用
    多路复用在网络中,也经常用到,比如HTTP2,多路就是一个线程有多个IO,如果还被阻塞,有一个,或多个成功了,就返回调用。需要线程遍历全部IO,判断是哪个IO有数据。例如Java网络里的socket 的 select() 函数,线程调用 select() 进入阻塞态,任何一个IO有数据了,线程就退出阻塞态,获得机会继续执行。
    (4)信号驱动IO
    给一个IO注册一个信号和信号触发的回调函数,一旦信号被触发,回调函数里读取数据。
    例如给 socket 注册一个“可读”的信号,当数据来了,可读的时候,信号被触发,执行回调函数从内核cache复制数据到用户空间。
    (5)异步IO
    异步IO中,操作系统完成了数据从内核到用户空间的拷贝后,以信号的方式通知用户线程可以下一步操作。省去了用户线程阻塞下来拷贝数据的过程。

    用户空间和内核空间:

    内核空间:Java的IO操作都是由操作系统来执行的,比如磁盘到内存,内存到磁盘,都是发生在内核空间的。
    用户空间:JVM的堆(进程的内存空间)在这里的程序和指令是访问IO是--被限制的,只能通过内核去访问硬件,不能JVM直接访问IO,因为安全性问题。
    Linux就是这样子的,关于这个,体系比较庞大,都可以取一个专题了。

    原理

    (1)用户程序发起读操作,导致“ syscall read ”系统调用,操作系统接收到调用后,会创建一个缓冲区Buffer,用来存放需要读取的数据。
    (2)创建后,同时向IO设备(设备控制器)发送读取指令(包括读哪些内容),IO硬件接收到指令后,进行读取,并把数据通过DMA(直接存取器)放入内核空间的缓冲区中(Buffer)。
    (3)操作系统将内核缓冲区中的数据拷贝到用户空间的缓存中,用户发起写操作,导致 “syscall write ”系统调用,将会把一个 buffer 中的数据搬出去,写到磁盘文件,小文件为普通IO,大文件为zerocopy技术。(4)Buffer其实可以理解成一个中转站,在java,php,node中都会用到,比如页面静态化。

    相关文章

      网友评论

          本文标题:Node.Js与Java的IO流,及底层会发生的故事。

          本文链接:https://www.haomeiwen.com/subject/ospjoqtx.html