java 使用RandomAssessFile類多執行緒切片下載檔案之伺服器如何實現
阿新 • • 發佈:2018-12-10
因為之前寫的都是客戶端,不需要去管服務端,直接把檔案放伺服器裡面,直接訪問,伺服器(tomcat之類得)就會自動幫我們切片,之類的。然後我自己想測試一些直接訪問檔案和使用控制器io讀寫返回檔案哪個快一些(肯定是io)https://blog.csdn.net/yali_aini/article/details/81745883,然後測試,發現按照我之前的寫法無法做到切片下載,研究了下,解決了這個問題,所以拿出來分享下。
我們都知道,客戶端下載的時候設定了 請求標頭檔案,設定了 range 請求範圍,伺服器端獲取範圍,返回範圍段內的資料就ok了
這裡得注意點,因為 rang讀取是包頭包尾的,如果 1-2 讀取的就是 2 個,直接 2-1 的話就是一個,所以這裡計算真實讀取大小的時候得加個1,不然就會出現 讀取少了位元組得bug。
程式碼不怎麼難,一起看看把,注意看註釋,思路就是我上面說的,獲取客戶端請求得 range,然後按照範圍去讀取資料,然後返回就ok了
@RequestMapping("/downloadFile") public void getFile(String name, HttpServletRequest request , HttpServletResponse response) { try { System.out.println("下載的檔案地址:"+ name ); File file = new File(name); // 檔案寫出 BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream()); // 獲取檔案讀取 RandomAccessFile read = new RandomAccessFile(file, "rw"); // 斷點分部下載起始點 long startPos = 0; long endPos = -1; Enumeration<String> names = request.getHeaderNames(); while(names.hasMoreElements()) { String n = names.nextElement(); String rangeData = request.getHeader(n); System.out.println( n + "---" + request.getHeader(n) ); if("range".equalsIgnoreCase(n)) { if( rangeData !=null && rangeData.indexOf("=")!=-1 ) { String [] arr = rangeData.substring(rangeData.indexOf("=")+1).split("-"); if(arr.length==2) { startPos = Integer.valueOf(arr[0]); endPos = Integer.valueOf(arr[1]); System.out.println( startPos + "---" + endPos ); } } } } if(endPos!=-1) { read.seek(startPos); }else { endPos = file.length(); } // 設定返回型別 response.setContentType("multipart/form-data"); // 檔名轉碼一下,不然會出現中文亂碼 response.setHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode("--chenparty下載站--" + file.getName(),"UTF-8")); // 設定返回的檔案的大小 response.setContentLength((int)file.length()); byte [] b = new byte[1024]; int len = 0; // 獲取真實的讀取大小,因為 rang讀取是包頭包尾的,如果 1-2 讀取的就是 2 個,直接 2-1 的話就是一個,所以這裡得+1 long actualLen = endPos - startPos + 1 ; while(-1 != (len = read.read(b))) { if( actualLen - len >= 0 ) { out.write(b, 0, len); // 記錄實時的 真實讀取大小 actualLen -= len; }else { // 真實大小讀取完畢 out.write(b, 0, (int)actualLen); break; } } read.close(); out.close(); } catch (Exception e) { e.printStackTrace(); } }