首页 教程 开发语言 Java怎么使用NIO优化IO实现文件上传下载功能

Java怎么使用NIO优化IO实现文件上传下载功能

1 NIO的一些基础预备知识

Java中IO流类的体系中BIO与NIO:https://blog.csdn.net/ZGL_cyy/article/details/104326458

Java IO体系与NIO和BIO体系面试题 :https://blog.csdn.net/ZGL_cyy/article/details/122836368

为什么使用NIO:因为传统IO文件传输速率低,所以选择了NIO进行文件的下载操作。NIO还有一个好处就是其中零拷贝可以实现减少内存中数据的重复,减少CPU操作的效果。所以相对于传统IO,NIO有着效率高点的优势。

2 NIO为何较传统的io速度较快

就拿单个io过程来看,首先时间主要花在了用户态和内核态的转换上,其次,考虑将多个io的“合并”为一个io,这不就节省时间了吗

相应的NIO主要做了两方面的提升

1、避免了用户态和内核态的交换,直接操作内存,用户态和内核态的转换是很费时的,传统的io写入磁盘时,用户态的接口不能直接操作内存,而是通过操作系统调用内核态接口来进行io。

2、利用buffer减少io的次数,buffer化零为整”的写入方式因为大大减小了寻址/写入次数,所以就降低了硬盘的负荷。

3、IO 是基于流来读取的,而NIO则是基于块读取,面向流 的 I/O 系统一次一个字节地处理数据。一个输入流产生一个字节的数据,一个输出流消费一个字节的数据。为流式数据创建过滤器非常容易。链接几个过滤器,以便每个过滤器只负责单个复杂处理机制的一部分,这样也是相对简单的。不利的一面是,面向流的 I/O 通常相当慢。

一个 面向块 的 I/O 系统以块的形式处理数据。每一个操作都在一步中产生或者消费一个数据块。按块处理数据比按(流式的)字节处理数据要快得多。但是面向块的 I/O 缺少一些面向流的 I/O 所具有的优雅性和简单性。

4、非阻塞IO 和 异步IO的支持, 减少线程占有的栈空间,以及上下文切换

5、IO 多路复用的支持

6、Buffer 支持,所有读写操作都是基于 缓冲 来实现

7、NIO 支持 Direct Memory, 可以减少一次数据拷贝

8、Netty 零拷贝的支持

3 NIO实战上传下载

3.1 url下载文件

java NIO包提供了无缓冲情况下在两个通道之间直接传输字节的可能。

为了读来自URL的文件,需从URL流创建ReadableByteChannel :

ReadableByteChannel readableByteChannel = Channels.newChannel(url.openStream());

从ReadableByteChannel 读取字节将被传输至FileChannel:

FileOutputStream fileOutputStream = new FileOutputStream(FILE_NAME); FileChannel fileChannel = fileOutputStream.getChannel();

然后使用transferFrom方法,从ReadableByteChannel 类下载来自URL的字节传输到FileChannel:

fileOutputStream.getChannel().transferFrom(readableByteChannel, 0, Long.MAX_VALUE);

transferTo() 和 transferFrom() 方法比简单使用缓存从流中读更有效。依据不同的底层操作系统,数据可以直接从文件系统缓存传输到我们的文件,而不必将任何字节复制到应用程序内存中。

在Linux和UNIX系统上,这些方法使用零拷贝技术,减少了内核模式和用户模式之间的上下文切换次数。

工具类:

/**NIO文件下载工具类  * @author olalu  */ public class NioDownloadUtils {       /**      * @description:      * @param file: 要下在文件      * @return: void      */     public static void downloadDoc(File file,HttpServletResponse response) throws IOException {         OutputStream outputStream = response.getOutputStream();         String contentType = Files.probeContentType(Paths.get(file.getAbsolutePath()));         //设置响应头         response.setHeader("Content-Type", contentType);         response.setHeader("Content-Disposition", "attachment;filename="+ new String(file.getName().getBytes("utf-8"),"ISO8859-1"));         response.setContentLength((int) file.length());         //获取文件输入流         FileInputStream fileInputStream = new FileInputStream(file);         //获取输出流通道         WritableByteChannel writableByteChannel = Channels.newChannel(outputStream);         FileChannel fileChannel = fileInputStream.getChannel();         //采用零拷贝的方式实现文件的下载         fileChannel.transferTo(0,fileChannel.size(),writableByteChannel);         //关闭对应的资源         fileChannel.close();         outputStream.flush();         writableByteChannel.close();     }       public static void downloadDoc(String path,HttpServletResponse response) throws IOException {         File file = new File(path);         if (!file.exists()){             throw new RuntimeException("文件不存在");         }         downloadDoc(file,response);     }   }

3.2 通过NIO上传文件

/**  * 文件上传方法  */ public static Result uploading(MultipartFile file) {     //获取文件名     String realName = file.getOriginalFilename();     String newName = null;     if(realName != null && realName != ""){         //获取文件后缀         String suffixName = realName.substring(realName.lastIndexOf("."));         //生成新名字         newName = UUID.randomUUID().toString().replaceAll("-", "")+suffixName;     }else {         return Result.fail("文件名不可为空");     }     //创建流     FileInputStream fis = null;     FileOutputStream fos = null;     //创建通道     FileChannel inChannel = null;     FileChannel outChannel = null;     try {         fis = (FileInputStream)file.getInputStream();         //开始上传         fos = new FileOutputStream(UPLOAD_URL+"\\"+newName);         //通道间传输         inChannel = fis.getChannel();         outChannel = fos.getChannel();         //上传         inChannel.transferTo(0,inChannel.size(),outChannel);     }catch (IOException e){         return Result.fail("文件上传路径错误");     }finally {         //关闭资源         try {             if (fis != null) {                 fis.close();             }             if (fos != null) {                 fos.close();             }             if (inChannel != null) {                 inChannel.close();             }             if (outChannel != null) {                 outChannel.close();             }         } catch (IOException e) {             e.printStackTrace();         }     }     return Result.ok(newName); }

评论(0)条

提示:请勿发布广告垃圾评论,否则封号处理!!

    猜你喜欢
    【MySQL】用户管理

    【MySQL】用户管理

     服务器/数据库  2个月前  2.15k

    我们推荐使用普通用户对数据的访问。而root作为管理员可以对普通用户对应的权限进行设置和管理。如给张三和李四这样的普通用户权限设定后。就只能操作给你权限的库了。

    Cursor Rules 让开发效率变成10倍速

    Cursor Rules 让开发效率变成10倍速

     服务器/数据库  2个月前  1.21k

    在AI与编程的交汇点上,awesome-cursorrules项目犹如一座灯塔,指引着开发者们驶向更高效、更智能的编程未来。无论你是经验丰富的老手,还是刚入行的新人,这个项目都能为你的编程之旅增添一抹亮色。这些规则文件就像是你私人定制的AI助手,能够根据你的项目需求和个人偏好,精确地调教AI的行为。突然间,你会发现AI不仅能理解Next.js的最佳实践,还能自动应用TypeScript的类型检查,甚至主动提供Tailwind CSS的类名建议。探索新的应用场景,推动AI辅助编程的边界。

    探索Django 5: 从零开始,打造你的第一个Web应用

    探索Django 5: 从零开始,打造你的第一个Web应用

     服务器/数据库  2个月前  1.13k

    Django 是一个开放源代码的 Web 应用程序框架,由 Python 写成。它遵循 MVT(Model-View-Template)的设计模式,旨在帮助开发者高效地构建复杂且功能丰富的 Web 应用程序。随着每个版本的升级,Django 不断演变,提供更多功能和改进,让开发变得更加便捷。《Django 5 Web应用开发实战》集Django架站基础、项目实践、开发经验于一体,是一本从零基础到精通Django Web企业级开发技术的实战指南《Django 5 Web应用开发实战》内容以。

    MySQL 的mysql_secure_installation安全脚本执行过程介绍

    MySQL 的mysql_secure_installation安全脚本执行过程介绍

     服务器/数据库  2个月前  1.08k

    mysql_secure_installation 是 MySQL 提供的一个安全脚本,用于提高数据库服务器的安全性

    【MySQL基础篇】概述及SQL指令:DDL及DML

    【MySQL基础篇】概述及SQL指令:DDL及DML

     服务器/数据库  2个月前  483

    数据库是长期存储在计算机内的、有组织的、可共享的、统一管理的大量数据的集合。数据库不仅仅是数据的简单堆积,而是遵循一定的规则和模式进行组织和管理的。数据库中的数据可以包括文本、数字、图像、音频等各种类型的信息。

    Redis中的哨兵(Sentinel)

    Redis中的哨兵(Sentinel)

     服务器/数据库  2个月前  309

    ​ 上篇文章我们讲述了Redis中的主从复制(Redis分布式系统中的主从复制-CSDN博客),本篇文章针对主从复制中的问题引出Redis中的哨兵,希望本篇文章会对你有所帮助。