您好,登錄后才能下訂單哦!
本篇文章給大家分享的是有關如何利用Java實現一個斷點續傳功能,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
功能實現的主要代碼:
import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /* * Encode:UTF-8 * * Author:zhiming.xu * * 多線程的斷點下載程序,根據輸入的url和指定線程數,來完成斷點續傳功能。 * * 每個線程支負責某一小段的數據下載;再通過RandomAccessFile完成數據的整合。 */ public class MultiTheradDownLoad { private String filepath = null; private String filename = null; private String tmpfilename = null; private int threadNum = 0; private CountDownLatch latch = null;//設置一個計數器,代碼內主要用來完成對緩存文件的刪除 private long fileLength = 0l; private long threadLength = 0l; private long[] startPos;//保留每個線程下載數據的起始位置。 private long[] endPos;//保留每個線程下載數據的截止位置。 private boolean bool = false; private URL url = null; //有參構造函數,先構造需要的數據 public MultiTheradDownLoad(String filepath, int threadNum) { this.filepath = filepath; this.threadNum = threadNum; startPos = new long[this.threadNum]; endPos = new long[this.threadNum]; latch = new CountDownLatch(this.threadNum); } /* * 組織斷點續傳功能的方法 */ public void downloadPart() { File file = null; File tmpfile = null; HttpURLConnection httpcon = null; //在請求url內獲取文件資源的名稱;此處沒考慮文件名為空的情況,此種情況可能需使用UUID來生成一個唯一數來代表文件名。 filename = filepath.substring(filepath.lastIndexOf('/') + 1, filepath .contains("?") ? filepath.lastIndexOf('?') : filepath.length()); tmpfilename = filename + "_tmp"; try { url = new URL(filepath); httpcon = (HttpURLConnection) url.openConnection(); setHeader(httpcon); fileLength = httpcon.getContentLengthLong();//獲取請求資源的總長度。 file = new File(filename); tmpfile = new File(tmpfilename); threadLength = fileLength / threadNum;//每個線程需下載的資源大小。 System.out.println("fileName: " + filename + " ," + "fileLength= " + fileLength + " the threadLength= " + threadLength); if (file.exists() && file.length() == fileLength) { System.out .println("the file you want to download has exited!!"); return; } else { setBreakPoint(startPos, endPos, tmpfile); ExecutorService exec = Executors.newCachedThreadPool(); for (int i = 0; i < threadNum; i++) { exec.execute(new DownLoadThread(startPos[i], endPos[i], this, i, tmpfile, latch)); } latch.await();//當你的計數器減為0之前,會在此處一直阻塞。 exec.shutdown(); } } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } if (file.length() == fileLength) { if (tmpfile.exists()) { System.out.println("delect the temp file!!"); tmpfile.delete(); } } } /* * 斷點設置方法,當有臨時文件時,直接在臨時文件中讀取上次下載中斷時的斷點位置。沒有臨時文件,即第一次下載時,重新設置斷點。 * * rantmpfile.seek()跳轉到一個位置的目的是為了讓各個斷點存儲的位置盡量分開。 * * 這是實現斷點續傳的重要基礎。 */ private void setBreakPoint(long[] startPos, long[] endPos, File tmpfile) { RandomAccessFile rantmpfile = null; try { if (tmpfile.exists()) { System.out.println("the download has continued!!"); rantmpfile = new RandomAccessFile(tmpfile, "rw"); for (int i = 0; i < threadNum; i++) { rantmpfile.seek(8 * i + 8); startPos[i] = rantmpfile.readLong(); rantmpfile.seek(8 * (i + 1000) + 16); endPos[i] = rantmpfile.readLong(); System.out.println("the Array content in the exit file: "); System.out.println("thre thread" + (i + 1) + " startPos:" + startPos[i] + ", endPos: " + endPos[i]); } } else { System.out.println("the tmpfile is not available!!"); rantmpfile = new RandomAccessFile(tmpfile, "rw"); //最后一個線程的截止位置大小為請求資源的大小 for (int i = 0; i < threadNum; i++) { startPos[i] = threadLength * i; if (i == threadNum - 1) { endPos[i] = fileLength; } else { endPos[i] = threadLength * (i + 1) - 1; } rantmpfile.seek(8 * i + 8); rantmpfile.writeLong(startPos[i]); rantmpfile.seek(8 * (i + 1000) + 16); rantmpfile.writeLong(endPos[i]); System.out.println("the Array content: "); System.out.println("thre thread" + (i + 1) + " startPos:" + startPos[i] + ", endPos: " + endPos[i]); } } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { if (rantmpfile != null) { rantmpfile.close(); } } catch (IOException e) { e.printStackTrace(); } } } /* * 實現下載功能的內部類,通過讀取斷點來設置向服務器請求的數據區間。 */ class DownLoadThread implements Runnable { private long startPos; private long endPos; private MultiTheradDownLoad task = null; private RandomAccessFile downloadfile = null; private int id; private File tmpfile = null; private RandomAccessFile rantmpfile = null; private CountDownLatch latch = null; public DownLoadThread(long startPos, long endPos, MultiTheradDownLoad task, int id, File tmpfile, CountDownLatch latch) { this.startPos = startPos; this.endPos = endPos; this.task = task; this.tmpfile = tmpfile; try { this.downloadfile = new RandomAccessFile(this.task.filename, "rw"); this.rantmpfile = new RandomAccessFile(this.tmpfile, "rw"); } catch (FileNotFoundException e) { e.printStackTrace(); } this.id = id; this.latch = latch; } @Override public void run() { HttpURLConnection httpcon = null; InputStream is = null; int length = 0; System.out.println("the thread " + id + " has started!!"); while (true) { try { httpcon = (HttpURLConnection) task.url.openConnection(); setHeader(httpcon); //防止網絡阻塞,設置指定的超時時間;單位都是ms。超過指定時間,就會拋出異常 httpcon.setReadTimeout(20000);//讀取數據的超時設置 httpcon.setConnectTimeout(20000);//連接的超時設置 if (startPos < endPos) { //向服務器請求指定區間段的數據,這是實現斷點續傳的根本。 httpcon.setRequestProperty("Range", "bytes=" + startPos + "-" + endPos); System.out .println("Thread " + id + " the total size:---- " + (endPos - startPos)); downloadfile.seek(startPos); if (httpcon.getResponseCode() != HttpURLConnection.HTTP_OK && httpcon.getResponseCode() != HttpURLConnection.HTTP_PARTIAL) { this.task.bool = true; httpcon.disconnect(); downloadfile.close(); System.out.println("the thread ---" + id + " has done!!"); latch.countDown();//計數器自減 break; } is = httpcon.getInputStream();//獲取服務器返回的資源流 long count = 0l; byte[] buf = new byte[1024]; while (!this.task.bool && (length = is.read(buf)) != -1) { count += length; downloadfile.write(buf, 0, length); //不斷更新每個線程下載資源的起始位置,并寫入臨時文件;為斷點續傳做準備 startPos += length; rantmpfile.seek(8 * id + 8); rantmpfile.writeLong(startPos); } System.out.println("the thread " + id + " total load count: " + count); //關閉流 is.close(); httpcon.disconnect(); downloadfile.close(); rantmpfile.close(); } latch.countDown();//計數器自減 System.out.println("the thread " + id + " has done!!"); break; } catch (IOException e) { e.printStackTrace(); } finally { try { if (is != null) { is.close(); } } catch (IOException e) { e.printStackTrace(); } } } } } /* * 為一個HttpURLConnection模擬請求頭,偽裝成一個瀏覽器發出的請求 */ private void setHeader(HttpURLConnection con) { con.setRequestProperty( "User-Agent", "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.3) Gecko/2008092510 Ubuntu/8.04 (hardy) Firefox/3.0.3"); con.setRequestProperty("Accept-Language", "en-us,en;q=0.7,zh-cn;q=0.3"); con.setRequestProperty("Accept-Encoding", "aa"); con.setRequestProperty("Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7"); con.setRequestProperty("Keep-Alive", "300"); con.setRequestProperty("Connection", "keep-alive"); con.setRequestProperty("If-Modified-Since", "Fri, 02 Jan 2009 17:00:05 GMT"); con.setRequestProperty("If-None-Match", "\"1261d8-4290-df64d224\""); con.setRequestProperty("Cache-Control", "max-age=0"); con.setRequestProperty("Referer", "http://www.skycn.com/soft/14857.html"); } }
下面是測試代碼:
public class DownLoadTest { /** * @param args */ public static void main(String[] args) { String filepath = "http://127.0.0.1:8080/file/loadfile.mkv"; MultiTheradDownLoad load = new MultiTheradDownLoad(filepath ,4); load.downloadPart(); } }
以上就是如何利用Java實現一個斷點續傳功能,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。