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

 3104

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 :

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

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

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

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

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

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

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

工具类:

  1. /**NIO文件下载工具类
  2.  * @author olalu
  3.  */
  4. public class NioDownloadUtils {
  5.  
  6.     /**
  7.      * @description:
  8.      * @param file: 要下在文件
  9.      * @return: void
  10.      */
  11.     public static void downloadDoc(File file,HttpServletResponse response) throws IOException {
  12.         OutputStream outputStream = response.getOutputStream();
  13.         String contentType = Files.probeContentType(Paths.get(file.getAbsolutePath()));
  14.         //设置响应头
  15.         response.setHeader("Content-Type", contentType);
  16.         response.setHeader("Content-Disposition", "attachment;filename="+ new String(file.getName().getBytes("utf-8"),"ISO8859-1"));
  17.         response.setContentLength((int) file.length());
  18.         //获取文件输入流
  19.         FileInputStream fileInputStream = new FileInputStream(file);
  20.         //获取输出流通道
  21.         WritableByteChannel writableByteChannel = Channels.newChannel(outputStream);
  22.         FileChannel fileChannel = fileInputStream.getChannel();
  23.         //采用零拷贝的方式实现文件的下载
  24.         fileChannel.transferTo(0,fileChannel.size(),writableByteChannel);
  25.         //关闭对应的资源
  26.         fileChannel.close();
  27.         outputStream.flush();
  28.         writableByteChannel.close();
  29.     }
  30.  
  31.     public static void downloadDoc(String path,HttpServletResponse response) throws IOException {
  32.         File file = new File(path);
  33.         if (!file.exists()){
  34.             throw new RuntimeException("文件不存在");
  35.         }
  36.         downloadDoc(file,response);
  37.     }
  38.  
  39. }


3.2 通过NIO上传文件

  1. /**
  2.  * 文件上传方法
  3.  */
  4. public static Result uploading(MultipartFile file) {
  5.     //获取文件名
  6.     String realName = file.getOriginalFilename();
  7.     String newName = null;
  8.     if(realName != null && realName != ""){
  9.         //获取文件后缀
  10.         String suffixName = realName.substring(realName.lastIndexOf("."));
  11.         //生成新名字
  12.         newName = UUID.randomUUID().toString().replaceAll("-", "")+suffixName;
  13.     }else {
  14.         return Result.fail("文件名不可为空");
  15.     }
  16.     //创建流
  17.     FileInputStream fis = null;
  18.     FileOutputStream fos = null;
  19.     //创建通道
  20.     FileChannel inChannel = null;
  21.     FileChannel outChannel = null;
  22.     try {
  23.         fis = (FileInputStream)file.getInputStream();
  24.         //开始上传
  25.         fos = new FileOutputStream(UPLOAD_URL+"\\"+newName);
  26.         //通道间传输
  27.         inChannel = fis.getChannel();
  28.         outChannel = fos.getChannel();
  29.         //上传
  30.         inChannel.transferTo(0,inChannel.size(),outChannel);
  31.     }catch (IOException e){
  32.         return Result.fail("文件上传路径错误");
  33.     }finally {
  34.         //关闭资源
  35.         try {
  36.             if (fis != null) {
  37.                 fis.close();
  38.             }
  39.             if (fos != null) {
  40.                 fos.close();
  41.             }
  42.             if (inChannel != null) {
  43.                 inChannel.close();
  44.             }
  45.             if (outChannel != null) {
  46.                 outChannel.close();
  47.             }
  48.         } catch (IOException e) {
  49.             e.printStackTrace();
  50.         }
  51.     }
  52.     return Result.ok(newName);
  53. }
本文网址:https://www.zztuku.com/detail-14190.html
站长图库 - Java怎么使用NIO优化IO实现文件上传下载功能
申明:本文转载于《CSDN》,如有侵犯,请 联系我们 删除。

评论(0)条

您还没有登录,请 登录 后发表评论!

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

    编辑推荐