java中怎么利用多线程实现文件下载功能
创始人
2024-12-22 03:01:07
0次

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; // 总的下载线程数 private int threadNum; // 下载的链接地址 private String urlFile; // 是否下载开始 private boolean isStarted; // 用于监视何时合并文件存放Thread的list private List downloadList = new ArrayList();  public DownloadManager(String savePath, int threadNum, String urlFile) { super(); this.savePath = savePath; this.threadNum = threadNum; this.urlFile = urlFile; }  // 最终调用线程下载。本线程中调用分线程。 public void action() { new Thread(this).start(); }  public void run() { long t1 = System.currentTimeMillis(); System.out.println(t1); // 如果没有下载 , 就开始 , 并且将已经下载的变量值设为true if (!isStarted) { startDownload(); isStarted = true; } while (true) { // 初始化认为所有线程下载完成,逐个检查 boolean finish = true; // 如果有任何一个没完成,说明下载没完成,不能合并文件 for (DownloadThread thread : downloadList) { if (!thread.isFinish()) { finish = false; break; } } // 全部下载完成才为真 if (finish) { // 合并文件 mergeFiles(); // 跳出循环 , 下载结束 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() { // 得到每个线程开始值 , 下载字节数大小 int[][] posAndLength = getPosAndLength(); // 根据下载信息创建每个下载线程,并且启动他们。 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; }  // 具体细节求出每个线程的开始位置和文件下载大小 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 getDownloadList() { return downloadList; }  public void setDownloadList(List 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; // 监听单一线程下载是否完成 private boolean isFinish; // 本线程要下载的文件字节数 private int length; // 本线程向服务器发送请求时输入流的首位置 private int startPosition; // 保存的路径 private String savePath; // 要下载的文件 , 用于创建连接 private String url;  public void run() { HttpURLConnection conn = null; InputStream in = null; OutputStream out = null; try { System.out.println("正在执行的线程:" + whichThread); URL fileUrl = new URL(url); // 与服务器创建连接 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 + "-"); // 与服务器创建连接 conn.connect(); // 获得输入流 in = conn.getInputStream(); // 在硬盘上创建file1 , file2 , ...这样的文件 , 准备往里面写东西 out = new FileOutputStream(savePath + whichThread); // 用于写入的字节数组 byte[] bytes = new byte[4096]; // 一共下载了多少字节 int count = 0; // 单次读取的字节数 int read = 0; while ((read = in.read(bytes)) != -1) { // 检查一下是不是下载到了本线程需要的长度 if (length - count < bytes.length) { // 比如说本线程还需要900字节,但是已经读取1000 // 字节,则用要本线程总下载长度减去 // 已经下载的长度 read = length - count; } // 将准确的字节写入输出流 out.write(bytes, 0, read); // 已经下载的字节数加上本次循环字节数 count = count + read; // 如果下载字节达到本线程所需要字节数,消除循环, // 停止下载 if (count == length) { break; } } // 将监视变量设置为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(); }}

相关内容

热门资讯

空调连续工作多久需要停机休息 空调连续工作的时间取决于多个因素,包括空调的型号、功率、使用环境和维护情况等。一般来说,空调可以连续...
空调哪个符号是除湿的 空调除湿功能通常用一个水滴形状的符号表示。不同品牌和型号的空调遥控器可能会有所差异,但一般来说,除湿...
格力空调多长时间加一次氟利昂 1、一般情况下,空调对氟利昂的使用量是非常小的,所以在空调使用了四年左右的时候再考虑加也没问题。2、...
空调用多长时间需要加氟一次 一般情况下是五年。当出现以下这几种情况时,空调才需要加氟。1、空调的室内机和室外机各有两个接口,当接...
格调生活·C标思享荟:卡萨帝在... 9月12日的郑州龙湖金融岛,夜风轻拂,灯光璀璨。“C标时刻共传奇——卡萨帝思享荟暨20周年品牌盛典郑...
品牌价值破千亿、高端份额持续领... 9月12日,“C标时刻共传奇——卡萨帝思享荟暨20周年品牌盛典郑州站”在郑州龙湖金融岛举办。2026...
空调主板坏了修800还是换新的 空调主板坏了是修还是换新,需要考虑多个因素,以下是一些建议:1. 维修成本:如果维修费用较低,比如 ...
空调开多久需要休息多长时间 问题:空调开多久需要休息多长时间回答:1、在使用空调的过程中,一般空调开7~8个小时左右需要休息2~...
中央空调多长时间清洗一次 1、一般空调开机前清洗一次即可,空调开机中间时段再清洗一次,空调关机时清洗一次,这样比较合理。2、空...
中央空调氟利昂多久加一次 看空调制热和制冷程度,很多人的观点认为氟利昂必须一年放一次,其实这个观点是错误的,理解上面有点偏差,...