java多線程實現(xiàn)文件下載
更新時間:2021年08月26日 10:21:56 作者:ljlleo2013
這篇文章主要為大家詳細介紹了java多線程實現(xiàn)文件下載,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
本文實例為大家分享了java多線程實現(xiàn)文件下載的具體代碼,供大家參考,具體內(nèi)容如下
1、DownloadManager類
import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.List; public class DownloadManager implements Runnable { // 保存路徑 private String savePath; // 總的下載線程數(shù) private int threadNum; // 下載的鏈接地址 private String urlFile; // 是否下載開始 private boolean isStarted; // 用于監(jiān)視何時合并文件存放Thread的list private List<DownloadThread> downloadList = new ArrayList<DownloadThread>(); public DownloadManager(String savePath, int threadNum, String urlFile) { super(); this.savePath = savePath; this.threadNum = threadNum; this.urlFile = urlFile; } // 最終調(diào)用線程下載。本線程中調(diào)用分線程。 public void action() { new Thread(this).start(); } public void run() { long t1 = System.currentTimeMillis(); System.out.println(t1); // 如果沒有下載 , 就開始 , 并且將已經(jīng)下載的變量值設為true if (!isStarted) { startDownload(); isStarted = true; } while (true) { // 初始化認為所有線程下載完成,逐個檢查 boolean finish = true; // 如果有任何一個沒完成,說明下載沒完成,不能合并文件 for (DownloadThread thread : downloadList) { if (!thread.isFinish()) { finish = false; break; } } // 全部下載完成才為真 if (finish) { // 合并文件 mergeFiles(); // 跳出循環(huán) , 下載結束 break; } // 休息一會 , 減少cpu消耗 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } long t2 = System.currentTimeMillis(); System.out.println(t2); System.out.println("下載用時:" + (t2 -t1)); } public void startDownload() { // 得到每個線程開始值 , 下載字節(jié)數(shù)大小 int[][] posAndLength = getPosAndLength(); // 根據(jù)下載信息創(chuàng)建每個下載線程,并且啟動他們。 for (int i = 0; i < posAndLength.length; i++) { int pos = posAndLength[i][0]; int length = posAndLength[i][1]; DownloadThread downloadThread = new DownloadThread(i + 1, length, pos, savePath, urlFile); new Thread(downloadThread).start(); downloadList.add(downloadThread); } } /** * 獲得文件大小 * * @return 文件大小 */ public long getFileLength() { System.out.println("獲得文件大小 start......"); HttpURLConnection conn = null; long result = 0; try { URL url = new URL(urlFile); conn = (HttpURLConnection) url.openConnection(); // 使用Content-Length頭信息獲得文件大小 result = Long.parseLong(conn.getHeaderField("Content-Length")); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (conn != null) { conn.disconnect(); } } System.out.println("獲得文件大小 end......" + result); return result; } // 具體細節(jié)求出每個線程的開始位置和文件下載大小 public int[][] getPosAndLength() { int[][] result = new int[threadNum][2]; int fileLength = (int) getFileLength(); int every = fileLength % threadNum == 0 ? fileLength / threadNum : fileLength / threadNum + 1; for (int i = 0; i < result.length; i++) { int length = 0; if (i != result.length - 1) { length = every; } else { length = fileLength - i * every; } result[i][0] = i * every; result[i][1] = length; } return result; } // 合并文件 public void mergeFiles() { System.out.println("合并文件 start......"); OutputStream out = null; try { out = new FileOutputStream(savePath); for (int i = 1; i <= threadNum; i++) { InputStream in = new FileInputStream(savePath + i); byte[] bytes = new byte[2048]; int read = 0; while ((read = in.read(bytes)) != -1) { out.write(bytes, 0, read); out.flush(); } if (in != null) { in.close(); new File(savePath + i).delete(); } } } catch (Exception e) { e.printStackTrace(); } finally { if (out != null) { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } System.out.println("合并文件 end......"); } public String getSavePath() { return savePath; } public void setSavePath(String savePath) { this.savePath = savePath; } public int getThreadNum() { return threadNum; } public void setThreadNum(int threadNum) { this.threadNum = threadNum; } public String getUrlFile() { return urlFile; } public void setUrlFile(String urlFile) { this.urlFile = urlFile; } public boolean isStarted() { return isStarted; } public void setStarted(boolean isStarted) { this.isStarted = isStarted; } public List<DownloadThread> getDownloadList() { return downloadList; } public void setDownloadList(List<DownloadThread> downloadList) { this.downloadList = downloadList; } }
2、DownloadThread類
import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; public class DownloadThread implements Runnable { // 當前第幾個線程 , 用于給下載文件起名 file1 file2 file3 ... private int whichThread; // 監(jiān)聽單一線程下載是否完成 private boolean isFinish; // 本線程要下載的文件字節(jié)數(shù) private int length; // 本線程向服務器發(fā)送請求時輸入流的首位置 private int startPosition; // 保存的路徑 private String savePath; // 要下載的文件 , 用于創(chuàng)建連接 private String url; public void run() { HttpURLConnection conn = null; InputStream in = null; OutputStream out = null; try { System.out.println("正在執(zhí)行的線程:" + whichThread); URL fileUrl = new URL(url); // 與服務器創(chuàng)建連接 conn = (HttpURLConnection) fileUrl.openConnection(); // 下載使用get請求 conn.setRequestMethod("GET"); // 告訴服務器 , 我是火狐 , 不要不讓我下載。 conn.setRequestProperty( "User-Agent", "Firefox Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3"); // 這里是設置文件輸入流的首位置 conn.setRequestProperty("Range", "bytes=" + startPosition + "-"); // 與服務器創(chuàng)建連接 conn.connect(); // 獲得輸入流 in = conn.getInputStream(); // 在硬盤上創(chuàng)建file1 , file2 , ...這樣的文件 , 準備往里面寫東西 out = new FileOutputStream(savePath + whichThread); // 用于寫入的字節(jié)數(shù)組 byte[] bytes = new byte[4096]; // 一共下載了多少字節(jié) int count = 0; // 單次讀取的字節(jié)數(shù) int read = 0; while ((read = in.read(bytes)) != -1) { // 檢查一下是不是下載到了本線程需要的長度 if (length - count < bytes.length) { // 比如說本線程還需要900字節(jié),但是已經(jīng)讀取1000 // 字節(jié),則用要本線程總下載長度減去 // 已經(jīng)下載的長度 read = length - count; } // 將準確的字節(jié)寫入輸出流 out.write(bytes, 0, read); // 已經(jīng)下載的字節(jié)數(shù)加上本次循環(huán)字節(jié)數(shù) count = count + read; // 如果下載字節(jié)達到本線程所需要字節(jié)數(shù),消除循環(huán), // 停止下載 if (count == length) { break; } } // 將監(jiān)視變量設置為true isFinish = true; } catch (Exception e) { e.printStackTrace(); } finally { // 最后進行輸入、輸出、連接的關閉 if (in != null) { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } if (out != null) { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } if (conn != null) { conn.disconnect(); } } } public int getStartPosition() { return startPosition; } public void setStartPosition(int startPosition) { this.startPosition = startPosition; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public int getWhichThread() { return whichThread; } public void setWhichThread(int whichThread) { this.whichThread = whichThread; } public int getLength() { return length; } public void setLength(int length) { this.length = length; } public String getSavePath() { return savePath; } public void setSavePath(String savePath) { this.savePath = savePath; } public DownloadThread(int whichThread, int length, int startPosition, String savePath, String url) { super(); this.whichThread = whichThread; this.length = length; this.startPosition = startPosition; this.savePath = savePath; this.url = url; } public DownloadThread() { super(); } public boolean isFinish() { return isFinish; } public void setFinish(boolean isFinish) { this.isFinish = isFinish; } }
3、TestDownload測試類
public class TestDownload { public static void main(String[] args) { DownloadManager downloadManager = new DownloadManager("d:/upload/09018417.zip" , 5 , "http://10.1.2.65:8080/cetvossFront/09018417.zip"); downloadManager.action(); } }
代碼已經(jīng)測試可以運行!
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
SpringBoot項目訪問任意接口出現(xiàn)401錯誤的解決方案
今天小編就為大家分享一篇關于SpringBoot項目訪問任意接口出現(xiàn)401錯誤的解決方案,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-01-01springboot啟動時如何指定spring.profiles.active
這篇文章主要介紹了springboot啟動時如何指定spring.profiles.active問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-04-04Java并發(fā)編程中的CyclicBarrier線程屏障詳解
這篇文章主要介紹了Java并發(fā)編程中的CyclicBarrier線程屏障詳解,2023-12-12利用Java如何實現(xiàn)將二維數(shù)組轉(zhuǎn)化為鏈式儲存
鏈式結構不要求邏輯上相鄰的節(jié)點在物理位置上也相鄰,節(jié)點間的邏輯關系是由附加的指針字段表示的,通常借助于程序設計中的指針結構來實現(xiàn),這篇文章主要給大家介紹了關于利用Java如何實現(xiàn)將二維數(shù)組轉(zhuǎn)化為鏈式儲存的相關資料,需要的朋友可以參考下2021-12-12深入分析@Resource和@Autowired注解區(qū)別
這篇文章主要為大家介紹了深入分析@Resource和@Autowired注解區(qū)別,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-04-04