Netty学习(3):文件操作

Stella981
• 阅读 720

概述

在 Netty学习(2)中,我们先浅浅认识了 NIO 的3大核心组件,现在就让我们针对其深入学习,通过一些简单的文件操作来深入理解其中的 BufferChannel 的概念。

文件写入

将内存中的数据写入到文件中,如果文件不存在,那么就新建文件。

// 数据 -> 文件
    private static void dataToFile(String data, String filePath) {
        // 构建输出流  ->   从输出流中获取 channel
        try (FileOutputStream fileOutputStream = new FileOutputStream(filePath);
             FileChannel fileChannel = fileOutputStream.getChannel()) {

            // 设置缓冲区
            ByteBuffer byteBuffer = ByteBuffer.allocate(BYTE_BUFFER_LENGTH);

            // 将需要读写的数据放到缓冲区
            int i = 0;
            int length = data.getBytes().length;
            // 一次就可以读完
            if (BYTE_BUFFER_LENGTH > data.getBytes().length) {
                byteBuffer.put(data.getBytes(), i, data.getBytes().length);
                byteBuffer.flip();
                fileChannel.write(byteBuffer);
            } else {
                // 一次读不完  需要循环读取
                for (int temp = 0; temp < data.getBytes().length; temp += BYTE_BUFFER_LENGTH) {
                    byteBuffer.clear();
                    byteBuffer.put(data.getBytes(), temp, BYTE_BUFFER_LENGTH);
                    // 翻转缓冲区,可以对外读
                    // 这里的 flip() 是重点,其可以将Buffer的属性重置,可以对外写
                    byteBuffer.flip();
                    // 将缓冲区内的数据写到 channel中
                    fileChannel.write(byteBuffer);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

这样,我们就写完了一个文件写入的函数,在需要时传入指定的字符串即可。

文件读取

从文件中读取数据,并将其输出到控制台中。

 // 文件 -> 内存
    private static void dataFromFile(String filePath) {
        File file = new File(filePath);
        // 从输入流中获取 channel
        try (FileInputStream fileInputStream = new FileInputStream(file);
             FileChannel channel = fileInputStream.getChannel()) {

            // 分配缓冲区
            ByteBuffer byteBuffer = ByteBuffer.allocate(BYTE_BUFFER_LENGTH);
            StringBuilder result = new StringBuilder();
            while (true) {
                byteBuffer.clear();
                // 将 channel数据写到buffer中
                int read = channel.read(byteBuffer);
                // 因为byteBuffer大小原因,因此需要用一个中间字符串接受一下
                result.append(new String(byteBuffer.array()));
                if (read == -1) {
                    break;
                }
            }

            logger.info("从文本读取结果:{}", result);
        } catch (Exception e) {
            logger.error("文件读取错误,错误原因 :{}", e);
        }
    }

文件拷贝

用 NIO 来完成文件拷贝,有两种实现方式,一种是用 Buffer 完成两个文件之间数据的转移,另一种是直接使用 Channel 来完成文件复制。

Buffer 完成

通过 Buffer 来完成文件复制,步骤如下:

  1. 获取源文件(source)和目标文件(target)的 channel;

  2. 设置缓冲区;

  3. 在循环中,通过缓冲区,将 source 的数据写入到 target 的 channel 中,完成写入,即复制成功。

    // 将两个channel通过byteBuffer进行转移 private static void copyFileUseBuffer(String sourceFilePath, String targetFilePath) { File source = new File(sourceFilePath); File target = new File(targetFilePath); // 获取文件输入输出流 // 从输入输出流中获取输入输出 channel try (FileInputStream fileInputStream = new FileInputStream(source); FileOutputStream fileOutputStream = new FileOutputStream(target); FileChannel fileInputStreamChannel = fileInputStream.getChannel(); FileChannel fileOutputStreamChannel = fileOutputStream.getChannel()) { // 分配缓冲区 ByteBuffer byteBuffer = ByteBuffer.allocate(BYTE_BUFFER_LENGTH); // 将输入流中的数据写到缓冲区 // 这里需要循环读取,如果是大文件,不能直接建立一个很大的内存空间,直接全部放进去,并且还可能放不进去 while (true) { byteBuffer.clear(); int read = fileInputStreamChannel.read(byteBuffer); if (read == -1) { break; } // 翻转缓冲区 byteBuffer.flip(); // 将翻转后可以对外写的缓存区的内容写到输出流,从而形成文件 fileOutputStreamChannel.write(byteBuffer); } } catch (Exception e) { logger.error("文件复制错误,错误原因 :{0}", e); }

Channel 完成

但其实,Java 官方也考虑到这个需求,其内置了一个通道复制的函数,可以直接完成复制。

// 直接用channel的复制完成文件复制
    private static void copyFileUseChannelTransfer(String sourceFilePath, String targetFilePath) {
        File source = new File(sourceFilePath);
        File target = new File(targetFilePath);
        // 获取文件输入输出流
        // 从输入输出流中获取输入输出 channel
        try (FileInputStream fileInputStream = new FileInputStream(source);
             FileOutputStream fileOutputStream = new FileOutputStream(target);
             FileChannel fileInputStreamChannel = fileInputStream.getChannel();
             FileChannel fileOutputStreamChannel = fileOutputStream.getChannel()) {

            // 直接将输入channel复制到输出channel
            fileOutputStreamChannel.transferFrom(fileInputStreamChannel, fileInputStreamChannel.position(), fileInputStreamChannel.size());

        } catch (Exception e) {
            logger.error("文件复制错误,错误原因 :{0}", e);
        }
    }

总结

本文,我们通过文件的读取,写入,复制,从而理解了 Buffer 和 Channel 的作用和使用方式,在后续的网络编程中,我们还要用到这些操作方式,以便逐步深入到 Netty 的学习范围。

本文中代码已上传到 GitHub 上,地址为 https://github.com/wb1069003157/nettyPre-research ,欢迎大家来讨论,探讨。

Netty学习(3):文件操作

文章在公众号「iceWang」第一手更新,有兴趣的朋友可以关注公众号,第一时间看到笔者分享的各项知识点,谢谢!笔芯!

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Wesley13 Wesley13
3年前
Java多线程导致的的一个事物性问题
业务场景我们现在有一个类似于文件上传的功能,各个子站点接受业务,业务上传文件,各个子站点的文件需要提交到总站点保存,文件是按批次提交到总站点的,也就是说,一个批次下面约有几百个文件。      考虑到白天提交这么多文件会影响到子站点其他系统带宽,我们将分站点的文件提交到总站点这个操作过程独立出来,放到晚上来做,具体时间是晚上7:00到早上7:00。
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这