Java使用NIO優(yōu)化IO實現(xiàn)文件上傳下載功能
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:因為傳統(tǒng)IO文件傳輸速率低,所以選擇了NIO進行文件的下載操作。NIO還有一個好處就是其中零拷貝可以實現(xiàn)減少內(nèi)存中數(shù)據(jù)的重復,減少CPU操作的效果。所以相對于傳統(tǒng)IO,NIO有著效率高點的優(yōu)勢。
2 NIO為何較傳統(tǒng)的io速度較快
就拿單個io過程來看,首先時間主要花在了用戶態(tài)和內(nèi)核態(tài)的轉(zhuǎn)換上,其次,考慮將多個io的“合并”為一個io,這不就節(jié)省時間了嗎
相應的NIO主要做了兩方面的提升
1.避免了用戶態(tài)和內(nèi)核態(tài)的交換,直接操作內(nèi)存,用戶態(tài)和內(nèi)核態(tài)的轉(zhuǎn)換是很費時的,傳統(tǒng)的io寫入磁盤時,用戶態(tài)的接口不能直接操作內(nèi)存,而是通過操作系統(tǒng)調(diào)用內(nèi)核態(tài)接口來進行io。
2.利用buffer減少io的次數(shù),buffer化零為整”的寫入方式因為大大減小了尋址/寫入次數(shù),所以就降低了硬盤的負荷。
3.IO 是基于流來讀取的,而NIO則是基于塊讀取,面向流 的 I/O 系統(tǒng)一次一個字節(jié)地處理數(shù)據(jù)。一個輸入流產(chǎn)生一個字節(jié)的數(shù)據(jù),一個輸出流消費一個字節(jié)的數(shù)據(jù)。為流式數(shù)據(jù)創(chuàng)建過濾器非常容易。鏈接幾個過濾器,以便每個過濾器只負責單個復雜處理機制的一部分,這樣也是相對簡單的。不利的一面是,面向流的 I/O 通常相當慢。
一個 面向塊 的 I/O 系統(tǒng)以塊的形式處理數(shù)據(jù)。每一個操作都在一步中產(chǎn)生或者消費一個數(shù)據(jù)塊。按塊處理數(shù)據(jù)比按(流式的)字節(jié)處理數(shù)據(jù)要快得多。但是面向塊的 I/O 缺少一些面向流的 I/O 所具有的優(yōu)雅性和簡單性。
4.非阻塞IO 和 異步IO的支持, 減少線程占有的??臻g,以及上下文切換
5.IO 多路復用的支持
6.Buffer 支持,所有讀寫操作都是基于 緩沖 來實現(xiàn)
7.NIO 支持 Direct Memory, 可以減少一次數(shù)據(jù)拷貝
8.Netty 零拷貝的支持
3 NIO實戰(zhàn)上傳下載
3.1 url下載文件
java NIO包提供了無緩沖情況下在兩個通道之間直接傳輸字節(jié)的可能。
為了讀來自URL的文件,需從URL流創(chuàng)建ReadableByteChannel :
ReadableByteChannel readableByteChannel = Channels.newChannel(url.openStream());
從ReadableByteChannel 讀取字節(jié)將被傳輸至FileChannel:
FileOutputStream fileOutputStream = new FileOutputStream(FILE_NAME); FileChannel fileChannel = fileOutputStream.getChannel();
然后使用transferFrom方法,從ReadableByteChannel 類下載來自URL的字節(jié)傳輸?shù)紽ileChannel:
fileOutputStream.getChannel().transferFrom(readableByteChannel, 0, Long.MAX_VALUE);
transferTo() 和 transferFrom() 方法比簡單使用緩存從流中讀更有效。依據(jù)不同的底層操作系統(tǒng),數(shù)據(jù)可以直接從文件系統(tǒng)緩存?zhèn)鬏數(shù)轿覀兊奈募?,而不必將任何字?jié)復制到應用程序內(nèi)存中。
在Linux和UNIX系統(tǒng)上,這些方法使用零拷貝技術,減少了內(nèi)核模式和用戶模式之間的上下文切換次數(shù)。
工具類:
/**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();
//采用零拷貝的方式實現(xiàn)文件的下載
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("文件名不可為空");
}
//創(chuàng)建流
FileInputStream fis = null;
FileOutputStream fos = null;
//創(chuàng)建通道
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);
}
到此這篇關于Java使用NIO優(yōu)化IO實現(xiàn)文件上傳下載的文章就介紹到這了,更多相關Java NIO文件上傳下載內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Spring中@RabbitHandler和@RabbitListener的區(qū)別詳析
@RabbitHandler是用于處理消息的方法注解,它與@RabbitListener注解一起使用,這篇文章主要給大家介紹了關于Spring中@RabbitHandler和@RabbitListener區(qū)別的相關資料,需要的朋友可以參考下2024-02-02
springboot+zookeeper實現(xiàn)分布式鎖的示例代碼
本文主要介紹了springboot+zookeeper實現(xiàn)分布式鎖的示例代碼,文中根據(jù)實例編碼詳細介紹的十分詳盡,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-03-03
SpringCloud及Nacos服務注冊IP選擇問題解決方法
這篇文章主要介紹了SpringCloud及Nacos服務注冊IP選擇問題,為什么注冊的IP和真實IP不符合呢,原因是Nacos客戶端在注冊服務時會從機器網(wǎng)卡中選擇一個IP來注冊,所以,當注冊了的是非真實IP后,另一臺機器調(diào)用時是不可能調(diào)通的,知道問題原因就是解決方法,一起看看吧2024-01-01
Java如何實現(xiàn)Unicode和中文相互轉(zhuǎn)換
這篇文章主要介紹了Java如何實現(xiàn)Unicode和中文相互轉(zhuǎn)換問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-01-01
Java中的Map集合根據(jù)key值排序的實現(xiàn)
本文主要介紹了Java中的Map集合如何根據(jù)key值排序,包含使用TreeMap和使用lambda表達式和Stream流兩種方法,具有一定的參考價值,感興趣的可以了解一下2024-03-03
Java 將文件轉(zhuǎn)為字節(jié)數(shù)組知識總結(jié)及實例詳解
這篇文章主要介紹了Java 將文件轉(zhuǎn)為字節(jié)數(shù)組實例詳解的相關資料,需要的朋友可以參考下2016-12-12
Spring AOP攔截-三種方式實現(xiàn)自動代理詳解
這篇文章主要介紹了Spring AOP攔截-三種方式實現(xiàn)自動代理詳解,還是比較不錯的,這里分享給大家,供需要的朋友參考。2017-11-11

