1. 程式人生 > >java 使用RandomAssessFile類多執行緒切片下載檔案之伺服器如何實現

java 使用RandomAssessFile類多執行緒切片下載檔案之伺服器如何實現

因為之前寫的都是客戶端,不需要去管服務端,直接把檔案放伺服器裡面,直接訪問,伺服器(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();
		}
	}