java多線程實(shí)現(xiàn)文件下載
本文實(shí)例為大家分享了java多線程實(shí)現(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)視何時(shí)合并文件存放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)下載的變量值設(shè)為true if (!isStarted) { startDownload(); isStarted = true; } while (true) { // 初始化認(rèn)為所有線程下載完成,逐個(gè)檢查 boolean finish = true; // 如果有任何一個(gè)沒完成,說明下載沒完成,不能合并文件 for (DownloadThread thread : downloadList) { if (!thread.isFinish()) { finish = false; break; } } // 全部下載完成才為真 if (finish) { // 合并文件 mergeFiles(); // 跳出循環(huán) , 下載結(jié)束 break; } // 休息一會(huì) , 減少cpu消耗 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } long t2 = System.currentTimeMillis(); System.out.println(t2); System.out.println("下載用時(shí):" + (t2 -t1)); } public void startDownload() { // 得到每個(gè)線程開始值 , 下載字節(jié)數(shù)大小 int[][] posAndLength = getPosAndLength(); // 根據(jù)下載信息創(chuàng)建每個(gè)下載線程,并且啟動(dò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; } // 具體細(xì)節(jié)求出每個(gè)線程的開始位置和文件下載大小 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 { // 當(dāng)前第幾個(gè)線程 , 用于給下載文件起名 file1 file2 file3 ... private int whichThread; // 監(jiān)聽單一線程下載是否完成 private boolean isFinish; // 本線程要下載的文件字節(jié)數(shù) private int length; // 本線程向服務(wù)器發(fā)送請(qǐng)求時(shí)輸入流的首位置 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); // 與服務(wù)器創(chuàng)建連接 conn = (HttpURLConnection) fileUrl.openConnection(); // 下載使用get請(qǐng)求 conn.setRequestMethod("GET"); // 告訴服務(wù)器 , 我是火狐 , 不要不讓我下載。 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"); // 這里是設(shè)置文件輸入流的首位置 conn.setRequestProperty("Range", "bytes=" + startPosition + "-"); // 與服務(wù)器創(chuàng)建連接 conn.connect(); // 獲得輸入流 in = conn.getInputStream(); // 在硬盤上創(chuàng)建file1 , file2 , ...這樣的文件 , 準(zhǔn)備往里面寫東西 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; } // 將準(zhǔn)確的字節(jié)寫入輸出流 out.write(bytes, 0, read); // 已經(jīng)下載的字節(jié)數(shù)加上本次循環(huán)字節(jié)數(shù) count = count + read; // 如果下載字節(jié)達(dá)到本線程所需要字節(jié)數(shù),消除循環(huán), // 停止下載 if (count == length) { break; } } // 將監(jiān)視變量設(shè)置為true isFinish = true; } catch (Exception e) { e.printStackTrace(); } finally { // 最后進(jìn)行輸入、輸出、連接的關(guān)閉 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測(cè)試類
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)測(cè)試可以運(yùn)行!
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot項(xiàng)目訪問任意接口出現(xiàn)401錯(cuò)誤的解決方案
今天小編就為大家分享一篇關(guān)于SpringBoot項(xiàng)目訪問任意接口出現(xiàn)401錯(cuò)誤的解決方案,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-01-01SpringMVC使用第三方組件實(shí)現(xiàn)文件上傳
這篇文章主要介紹了SpringMVC使用第三方組件實(shí)現(xiàn)文件上傳,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08springboot啟動(dòng)時(shí)如何指定spring.profiles.active
這篇文章主要介紹了springboot啟動(dòng)時(shí)如何指定spring.profiles.active問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04一篇文章帶你入門java算術(shù)運(yùn)算符(加減乘除余,字符連接)
這篇文章主要介紹了Java基本數(shù)據(jù)類型和運(yùn)算符,結(jié)合實(shí)例形式詳細(xì)分析了java基本數(shù)據(jù)類型、數(shù)據(jù)類型轉(zhuǎn)換、算術(shù)運(yùn)算符、邏輯運(yùn)算符等相關(guān)原理與操作技巧,需要的朋友可以參考下2021-08-08Java并發(fā)編程中的CyclicBarrier線程屏障詳解
這篇文章主要介紹了Java并發(fā)編程中的CyclicBarrier線程屏障詳解,2023-12-12利用Java如何實(shí)現(xiàn)將二維數(shù)組轉(zhuǎn)化為鏈?zhǔn)絻?chǔ)存
鏈?zhǔn)浇Y(jié)構(gòu)不要求邏輯上相鄰的節(jié)點(diǎn)在物理位置上也相鄰,節(jié)點(diǎn)間的邏輯關(guān)系是由附加的指針字段表示的,通常借助于程序設(shè)計(jì)中的指針結(jié)構(gòu)來實(shí)現(xiàn),這篇文章主要給大家介紹了關(guān)于利用Java如何實(shí)現(xiàn)將二維數(shù)組轉(zhuǎn)化為鏈?zhǔn)絻?chǔ)存的相關(guān)資料,需要的朋友可以參考下2021-12-12深入分析@Resource和@Autowired注解區(qū)別
這篇文章主要為大家介紹了深入分析@Resource和@Autowired注解區(qū)別,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04