標籤:下載 java webservice
一、 前言
本文講述如何通過webservice介面,從服務端下載檔案、報告到用戶端。適用於跨系統間的檔案互動,傳輸檔案不大的情況(控制在幾百M以內)。對於這種情況搭建一個FTP環境,增加了系統部署的複雜度和系統對外暴露的介面。通過在服務端讀取檔案,返回位元組流到用戶端的方式比較簡單。
下面採用restful的介面形式,滿足SOA架構介面要求。如下代碼拷貝到eclipse中即可運行,功能自測試回合正常。範例代碼的服務端和用戶端在同一台PC上運行,放到不同PC上運行改一下發布服務和請求服務的IP地址。
二、 環境準備
2.1 CXF組件:Java端用於發布WebService服務的開源組件,內部內建jetty Web容器。百度官網下載。
2.2 Eclipse:Java開發IDE。
三、 檔案下載服務端開發3.1 建立服務端Java項目,匯入CXF lib目錄下的Jar包。3.2 定義restful的WebService介面,用於下載檔案。
/** * 下載報告檔案WebService介面, 對於大於20M的檔案分多次傳輸。這裡不對檔案先讀取緩衝,再分批返回; * 而是每次重新讀取檔案,目的是為了讓本服務無狀態,能夠通過ngnix反向 Proxy多個執行個體,解決服務的可靠性 * 和負載平衡問題。這樣做有一個風險,在分批傳送過程中,如果檔案被修改或者刪除了將導致檔案讀取失敗。 * * @author Elon * @version 1.0 2015-06-30 */@Path("/DownloadFileWS")public class DownloadFileWS { // 單次傳送最大位元組數20M。 private final static int maxsize_once; static { maxsize_once = 1024 * 1024 * 20; } /** * 下載檔案。讀取檔案內容轉換為位元組流,大於10M分多次傳輸。 * @param req 請求參數 * @return 下載響應 */ @POST @Path("/downloadFile") public DownloadFileResponseVO downloadFile(DownloadFileRequestVO req){ try { return readFileByte(req); } catch (IOException e) { e.printStackTrace(); DownloadFileResponseVO vo = new DownloadFileResponseVO(); vo.setErrCode(DownloadErrCodeEnum.READ_FILE_EXCEPTION); return vo; } } /** * 讀取檔案內容,構建檔案位元組流返回對象。 * @param req 請求參數 * @return 讀取檔案傳回值。 * @throws IOException IO異常 */ private DownloadFileResponseVO readFileByte(DownloadFileRequestVO req) throws IOException { DownloadFileResponseVO vo = new DownloadFileResponseVO(); // 擷取判斷檔案最近修改時間 File fileObject = new File(req.getFilePath()); final long fileLastModifiedTime = fileObject.lastModified(); // 判斷分批傳過程中檔案是否修改 if (fileLastModifiedTime != req.getFileLastModifiedTime() && req.getFileLastModifiedTime() != -1) { vo.setErrCode(DownloadErrCodeEnum.FILE_HAS_MODIFIED_WHILE_DOWNLOAD); return vo; } // 讀取檔案位元組流。 ByteArrayOutputStream fileStream = new ByteArrayOutputStream(1024); FileInputStream file = new FileInputStream(req.getFilePath()); byte[] readbuff = new byte[1024]; while(file.read(readbuff) != -1) { fileStream.write(readbuff); } file.close(); // 構建返迴文件位元組資訊。超過20M, 一次只返回20M。 final byte[] fileBuff = fileStream.toByteArray(); int end = 0; if (fileBuff.length - req.getStart() > maxsize_once) { end = req.getStart() + maxsize_once; vo.setEof(false); } else { end = fileBuff.length; vo.setEof(true); } // 拷貝[start, end)範圍內的位元組到傳回值中。 vo.setFileByteBuff(Arrays.copyOfRange(fileBuff, req.getStart(), end)); vo.setStart(end); vo.setErrCode(DownloadErrCodeEnum.DOWN_LOAD_SUCCESS); vo.setFileLastModifiedTime(fileLastModifiedTime); fileStream.close(); return vo; }}
3.3 介面中使用輸入參數、傳回值、錯誤碼定義
3.3.1 輸入參數類型定義
/** * 下載檔案請求參數類型。 * * @author Elon * @version 1.0 2015-06-30 */@XmlRootElement(name = "DownloadFileRequest")public class DownloadFileRequestVO implements Serializable{ /** * 序列化編碼 */ private static final long serialVersionUID = 3142085277564296839L; // 檔案路徑 private String filePath; // 讀檔案資料起始位置 private int start; // 第一次讀取檔案時間(用於在分批傳送檔案過程中判斷檔案是否被修改了) private long fileLastModifiedTime; DownloadFileRequestVO() { setFilePath(""); setStart(0); setFileLastModifiedTime(-1); } public String getFilePath() { return filePath; } public void setFilePath(String filePath) { this.filePath = filePath; } public int getStart() { return start; } public void setStart(int start) { this.start = start; } public long getFileLastModifiedTime() { return fileLastModifiedTime; } public void setFileLastModifiedTime(long fileLastModifiedTime) { this.fileLastModifiedTime = fileLastModifiedTime; } @Override public String toString() { return "filePath: " + filePath + "\n" + "start: " + String.valueOf(start); }}
3.3.2 傳回值類型定義
/** * 檔案下載介面傳回值類型 * * @author Elon * @version 1.0 2015-06-30 */@XmlRootElement(name = "FileVO")public class DownloadFileResponseVO implements Serializable{ /** * 序列化編號 */ private static final long serialVersionUID = -2218217669316014388L; // 檔案位元組流緩衝 private byte[] fileByteBuff; // 標識檔案傳輸是否結束 private boolean eof; // 下一批讀取檔案資料起始位置 private int start; // 第一次讀取檔案時間(用於在分批傳送檔案過程中判斷檔案是否被修改了) private long fileLastModifiedTime; // 錯誤碼 private DownloadErrCodeEnum errCode; public DownloadFileResponseVO() { setFileByteBuff(null); setEof(false); setStart(0); setErrCode(DownloadErrCodeEnum.ERR_CODE_NA); setFileLastModifiedTime(-1); } public byte[] getFileByteBuff() { return fileByteBuff; } public void setFileByteBuff(byte[] fileByteBuff) { this.fileByteBuff = fileByteBuff; } public boolean isEof() { return eof; } public void setEof(boolean eof) { this.eof = eof; } public int getStart() { return start; } public void setStart(int start) { this.start = start; } public DownloadErrCodeEnum getErrCode() { return errCode; } public void setErrCode(DownloadErrCodeEnum errCode) { this.errCode = errCode; } public long getFileLastModifiedTime() { return fileLastModifiedTime; } public void setFileLastModifiedTime(long fileLastModifiedTime) { this.fileLastModifiedTime = fileLastModifiedTime; } @Override public String toString() { return "fileByteBuff: " + fileByteBuff.toString() + "\n" + "eof: " + String.valueOf(eof) + "\n" + "start: " + String.valueOf(start); }}
3.3.3 錯誤碼枚舉類型定義
/** * 下載檔案錯誤碼 * @author Elon * @version 1.0 2015-06-30 */public enum DownloadErrCodeEnum { DOWN_LOAD_SUCCESS, // 下載成功 READ_FILE_EXCEPTION, // 讀取檔案異常 FILE_HAS_MODIFIED_WHILE_DOWNLOAD, // 在分批下載檔案的過程中檔案發生了修改 ERR_CODE_NA, // 無效錯誤碼}
3.3.4 發布restful服務
public class StartServer { public static void main(String[] args) { publishWS(); } private static void publishWS() { JAXRSServerFactoryBean bean = new JAXRSServerFactoryBean(); bean.setAddress("http://10.61.67.246:10011/download"); bean.setServiceBean(new DownloadFileWS()); bean.create(); // 阻塞線程、等待外部訊息請求。 while(true) { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } } }}
四、 檔案下載用戶端開發4.1 建立用戶端Java項目,匯入CXF lib目錄下的Jar包。4.2 調用介面下載檔案,檔案位元組流寫入目標檔案儲存。
public class StartClient { public static void main(String[] args) throws IOException { downloadFileClient(); } private static void downloadFileClient() throws IOException { DownloadFileRequestVO req = new DownloadFileRequestVO(); req.setFilePath("D:\\TEMP\\測試報告壓縮包檔案.zip"); req.setStart(0); // 迴圈下載檔案位元組流,直到下載完檔案所有內容。 FileOutputStream out = new FileOutputStream("D://TEMP/123.zip"); while (true) { WebClient webClient = WebClient.create("http://10.61.67.246:10011"); webClient.encoding("UTF-8"); Response response = webClient.path("download/DownloadFileWS/downloadFile") .post(req); GenericType<DownloadFileResponseVO> vo = new GenericType<DownloadFileResponseVO>(){}; DownloadFileResponseVO rsp = response.readEntity(vo); webClient.close(); if (rsp.getErrCode() != DownloadErrCodeEnum.DOWN_LOAD_SUCCESS) { System.err.println("Download file err: " + rsp.getErrCode()); break; } // 將位元組流寫到檔案 out.write(rsp.getFileByteBuff()); if (rsp.isEof()) { break; } else { req.setStart(rsp.getStart()); req.setFileLastModifiedTime(rsp.getFileLastModifiedTime()); } } // 輸出、關閉檔案 try { out.flush(); out.close(); System.err.println("Download file success!"); } catch (Exception e) { e.printStackTrace(); } }}
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
通過Java WebService介面從服務端下載檔案