摘要:操作指引該文件服務組件的使用需要分為兩個部分,一個是服務端配置與啟動,一個是客戶端的配置與啟動。在調用文件服務返回的路徑的時候,需要用到服務端訪問文件的地址,進而訪問相應的文件內容。
本文所述文件服務組件在筆者此前一篇文章中已有闡述(基于netty的文件上傳下載組件),不過本文將基于之前這個實現再次進行升級改造,利用基于注解的方式進行自動裝配。1. 簡介 1.1 Netty簡介
Netty是一個異步事件驅動的網絡應用程序框架,用于快速開發可維護的高性能協議服務器和客戶端。關于其詳細的介紹可以參考Netty官方網站。
Netty is a NIO client server framework which enables quick and easy development of network applications such as protocol servers and clients. It greatly simplifies and streamlines network programming such as TCP and UDP socket server.
Netty主要特點:
Unified API for various transport types - blocking and non-blocking socket(統一API)
Based on a flexible and extensible event model which allows clear separation of concerns(事件模型)
Highly customizable thread model - single thread, one or more thread pools such as SEDA(線程模型)
True connectionless datagram socket support (since 3.1)(無連接數據報文Socket支持)
1.2 組件功能介紹該組件基于netty3.6.3實現,具有如下功能:文件上傳,文件替換,文件刪除,如果是圖片的話,還可以生成縮略圖等功能。使用簡單,只需要引入commons-doc-client-netty,即可以實現文件的以上操作。
本組件分為三個module,分別為:
commons-doc-server-netty:Netty實現文件服務組件的服務端
commons-doc-common:Netty文件服務組件公共組件
commons-doc-client-netty:Netty文件服務組件的客戶端
2. 服務端 2.1 功能簡介服務端組件實現以下功能:文件上傳,文件替換,文件刪除,如果是圖片的話,還可以生成縮略圖等功能。代碼結構如下圖所示,
所有的文件服務都是基于接口DocServerProcessor進行的,主要有以下幾個實現類:
UploadDocServerHandler實現文件上傳服務
ReplaceDocServerHandler實現文件替換服務
DeleteDocServerHandler實現文件刪除服務
CreateThumbPictureServerHandler實現創建圖片縮略圖服務
2.2 實現步驟具體實現步驟以文件上傳為例。
首先 org.fortune.doc.server.support.DocServerHandler類會持續監聽客戶端的請求,如果是文件處理動作,則會進入messageReceived方法進行相應的處理邏輯。該類定義了以下成員變量:
//http請求 private HttpRequest request; //是否需要斷點續傳作業 private boolean readingChunks; //接收到的文件內容 private final StringBuffer responseContent = new StringBuffer(); //解析收到的文件 private static final HttpDataFactory factory = new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE); //16384L //post請求的解碼類,它負責把字節解碼成Http請求。 private HttpPostRequestDecoder decoder; //請求參數 private RequestParam requestParams = new RequestParam();
該方法實現中,如果文件大小小于chunked的最小值,則直接進行文件上傳操作。否則,需要進行分塊處理。然后進行文件上傳操作。
文件大小小于1k的操作:
if (request.isChunked()) { //說明還沒有請求完成,繼續 this.readingChunks = true; LOGGER.info("文件分塊操作...."); } else { LOGGER.info("文件大小小于1KB,文件接收完成,直接進行相應的文件處理操作...."); //請求完成,則接收請求參數,進行初始化請求參數 RequestParamParser.parseParams(this.decoder, this.requestParams); //根據請求參數進行相應的文件操作 LOGGER.info("文件處理開始....requestParams參數解析:{}",requestParams); String result = DocServerHandlerFactory.process(this.requestParams); LOGGER.info("文件處理結束....FileServerHandlerFactory處理結果:{}",result); this.responseContent.append(result); //給客戶端響應信息 writeResponse(e.getChannel()); e.getFuture().addListener(ChannelFutureListener.CLOSE); }
需要分塊處理操作:
HttpChunk chunk = (HttpChunk) e.getMessage(); try { //chunk.getContent().capacity(); LOGGER.info("文件分塊操作....文件大小:{} bytes",chunk.getContent().capacity()); this.decoder.offer(chunk); } catch (HttpPostRequestDecoder.ErrorDataDecoderException e1) { e1.printStackTrace(); this.responseContent.append(e1.getMessage()); writeResponse(e.getChannel()); Channels.close(e.getChannel()); return; } if (chunk.isLast()) { //文件末尾 this.readingChunks = false; LOGGER.info("到達文件內容的末尾,進行相應的文件處理操作....start"); RequestParamParser.parseParams(this.decoder, this.requestParams); LOGGER.info("文件處理開始....requestParams參數解析:{}",requestParams); String result = DocServerHandlerFactory.process(this.requestParams); LOGGER.info("文件處理結束....FileServerHandlerFactory處理結果:{}",result); this.responseContent.append(result); //給客戶端響應信息 writeResponse(e.getChannel()); e.getFuture().addListener(ChannelFutureListener.CLOSE); LOGGER.info("到達文件內容的末尾,進行相應的文件處理操作....end"); }
以上操作主要有兩個注意點:
請求參數的解析工作(根據HttpDataType進行相應參數的賦值操作)
根據解析的參數進行相應的文件處理操作(根據文件操作類型,選擇相應的處理句柄進行文件處理)
3. 客戶端 3.1 功能簡介客戶端組件主要提供對外訪問服務端組件的接口,提供以下接口:文件上傳,文件替換,文件刪除,如果是圖片的話,還可以生成縮略圖等功能。代碼結構如下:
org.fortune.doc.client.DocClient類是對外提供接口的工具類,具有以下主要方法:
uploadFile 文件上傳,對應文件處理句柄類為:org.fortune.doc.client.handler.UploadDocClientHandler
deleteFile 刪除服務端文件,對應文件處理句柄類為:org.fortune.doc.client.handler.DeleteDocClientHandler
replaceFile 替換服務端文件,對應文件處理句柄類為:org.fortune.doc.client.handler.ReplaceDocClientHandler
createThumbPicture 生成縮略圖,對應文件處理句柄類為:org.fortune.doc.client.handler.CreateThumbPictureClientHandler
3.2 實現步驟實現步驟以上傳文件為例,其他類似實現。直接上代碼:
/** * 文件上傳 * @param file 需要上傳的文件 * @param fileName 文件名稱 * @param thumbMark 是否需要生成縮略圖 * @return * @author:landyChris */ public static String uploadFile(File file, String fileName, boolean thumbMark) { DocClientPipelineFactory clientPipelineFactory = new DocClientPipelineFactory(); //輔助類。用于幫助我們創建NETTY服務 ClientBootstrap bootstrap = createClientBootstrap(clientPipelineFactory); String strThumbMark = Constants.THUMB_MARK_NO; if (thumbMark) { strThumbMark = Constants.THUMB_MARK_YES; } //具體處理上傳文件邏輯 uploadFile(bootstrap, DocClientContainer.getInstance().getHost(), DocClientContainer.getInstance().getPort(), file, fileName, strThumbMark, DocClientContainer.getInstance().getUserName(), DocClientContainer.getInstance().getPassword()); Result result = clientPipelineFactory.getResult(); if ((result != null) && (result.isSuccess())) { return result.getFilePath(); } return null; }
具有三個參數,前面幾行代碼都是很一些netty的初始化工作,具體看一個私有方法uploadFile,如下代碼所示:
private static void uploadFile(ClientBootstrap bootstrap, String host, int port, File file, String fileName, String thumbMark, String userName, String pwd) { //1.構建uri對象 URI uri = getUri(host, port); //2.連接netty服務端 ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port)); //3.異步獲取Channel對象 Channel channel = future.awaitUninterruptibly().getChannel(); if (!future.isSuccess()) { future.getCause().printStackTrace(); bootstrap.releaseExternalResources(); return; } //4.初始化文件上傳句柄對象 AbstractDocClientHandler handler = new UploadDocClientHandler(host, uri, file, fileName, thumbMark, userName, pwd); //5.獲取Request對象 HttpRequest request = handler.getRequest(); //6.獲取Http數據處理工廠 HttpDataFactory factory = getHttpDataFactory(); //7.進行數據的包裝處理,主要是進行上傳文件所需要的參數的設置,此時調用的句柄是具體的UploadFileClientHandler對象 HttpPostRequestEncoder bodyRequestEncoder = handler .wrapRequestData(factory); //8.把request寫到管道中,傳輸給服務端 channel.write(request); //9.做一些關閉資源的動作 if (bodyRequestEncoder.isChunked()) { channel.write(bodyRequestEncoder).awaitUninterruptibly(); } bodyRequestEncoder.cleanFiles(); channel.getCloseFuture().awaitUninterruptibly(); bootstrap.releaseExternalResources(); factory.cleanAllHttpDatas(); }
主要有以下實現步驟:
構建uri對象
連接netty服務端
異步獲取Channel對象
初始化文件上傳句柄對象
獲取Request對象
獲取Http數據處理工廠
進行數據的包裝處理,主要是進行上傳文件所需要的參數的設置,此時調用的句柄是具體的UploadFileClientHandler對象
把request寫到管道中,傳輸給服務端
做一些關閉資源的動作
具體細節實現請參考github上的代碼。如果各位讀者喜歡的話,可以加個star哈。
該文件服務組件的使用需要分為兩個部分,一個是服務端配置與啟動,一個是客戶端的配置與啟動。
4.1 服務端配置與啟動 4.1.1 配置服務端的配置采用yml文件的配置,更加的簡潔明了,主要的注意點是文件存放位置的配置,在開發過程中,可以有兩種方式配置:
Idea自啟動方式:如果采用此種方式則需要把rootPath配置到工程路徑下(target目錄),如下所示:
# 在idea中執行的話,需要配置target目錄下的打包文件 rootPath: C: