多線程下載業務邏輯:html
一、URL請求獲取下載文件的大小、計算每一個線程下載的起始位置java
二、RandomAccessFile類在存儲空間佔位,隨機訪問流在多線程下可同時讀寫文件api
三、開啓線程,每一個線程負責下載文件的一部分,最後由隨機訪問流自動拼接文件,合成服務器
四、啓動線程多線程
此項目須要瞭解java.io.RandomAccessFile、java.net .HttpURLConnection 的具體用法,在線API參考文檔【http://tool.oschina.net/apidocs/apidoc?api=jdk-zh】dom
java.lang.Object java.net.URLConnection java.net.HttpURLConnection
重點:HttpURLConnection的父類URLConnection有個很是重要的 setRequestProperty() 方法,如下連接介紹了有關多線程下載,Range請求頭的使用例子。ide
詳情點擊:【http://www.tuicool.com/articles/viUfMjY】ui
import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; public class Demo { //定義全局變量address static String address = "http://localhost:8080/ThreadDownloadServer/Blood.mp4"; public static void main(String[] args) { //開啓三個線程下載 downloadFile(3); } /** * 功能:計算每一個線程下載的起始位置,並執行下載。 * @param 線程數 */ public static void downloadFile(int threadCount){ try { URL url = new URL(address); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(5000); conn.setReadTimeout(5000); //獲取文件字節總大小 int fileLength = conn.getContentLength(); System.out.println("文件總大小:"+fileLength/1024/1024+"MB"); int code = conn.getResponseCode(); if (code == 200) { //分配每一個線程"平均"下載字節數 int blockSize = fileLength/threadCount; //建立隨機訪問流,在本地爲文件佔位 RandomAccessFile raf = new RandomAccessFile("Blood.mp4", "rw"); //重點:假設文件只有10字節,分3個線程來下載,理解理解for循環代碼 for (int threadId = 0; threadId < threadCount; threadId++) { int startIndex = threadId*blockSize; int endIndex = (threadCount+1)*blockSize-1; if (threadId == threadCount) { endIndex = fileLength - 1; } //每循環一次,開啓一個線程執行下載,別漏了start()。 new ThreadDownload(threadId, startIndex, endIndex).start();; } conn.disconnect(); }else{ System.out.println("請求失敗,服務器響應碼:"+code); } } catch (MalformedURLException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * 功能:開啓線程下載,下載分配好的文件長度 * @author Administrator * @param 線程ID,下載開始位置,結束位置, */ public static class ThreadDownload extends Thread{ private int threadId; private int startIndex; private int endIndex; //構造方法接收3個參數初始化局部變量:線程ID、開始位置、結束位置 public ThreadDownload(int threadId, int startIndex, int endIndex) { this.threadId = threadId; this.startIndex = startIndex; this.endIndex = endIndex; } @Override public void run(){ try { URL url = new URL(address); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(5000); conn.setReadTimeout(5000); System.out.println("線程"+threadId+"下載開始位置"+startIndex+" 結束位置:"+endIndex); //設置請求頭,請求部分資源下載 conn.setRequestProperty("Range", "bytes:"+startIndex+"-"+endIndex); //服務器響應碼206表示請求部分資源成功 if (conn.getResponseCode()==206) { InputStream inputStream = conn.getInputStream(); RandomAccessFile raf = new RandomAccessFile(new File("Blood.mp4"), "rw"); //查找寫入開始位置 raf.seek(startIndex); byte array[] = new byte[1024]; int len = -1; while((len = inputStream.read(array))!=-1){ raf.write(array, 0, len); } inputStream.close(); raf.close(); conn.disconnect(); }else{ System.out.println("服務器響應碼:"+conn.getResponseCode()); } } catch (MalformedURLException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
刷新項目,可見項目已生成:this