Java 多线程断点下载文件_详解

基本原理:利用URLConnection获取要下载文件的长度、头部等相关信息,并设置响应的头部信息。并且通过URLConnection获取输入流,将文件分成指定的块,每一块单独开辟一个线程完成数据的读取、写入。通过输入流读取下载文件的信息,然后将读取的信息用RandomAccessFile随机写入到本地文件中。同时,每个线程写入的数据都文件指针也就是写入数据的长度,需要保存在一个临时文件中。这样当本次下载没有完成的时候,下次下载的时候就从这个文件中读取上一次下载的文件长度,然后继续接着上一次的位置开始下载。并且将本次下载的长度写入到这个文件中。

个人博客:

http://hoojo.cnblogs.com

http://blog.csdn.net/IBM_hoojo

email: hoojo_@126.com

 

一、下载文件信息类、实体

封装即将下载资源的信息

[java] view plaincopyprint?

  1. package com.hoo.entity; 
  2.  
  3. /**
  4. * <b>function:</b> 下载文件信息类
  5. * @author hoojo
  6. * @createDate 2011-9-21 下午05:14:58
  7. * @file DownloadInfo.java
  8. * @package com.hoo.entity
  9. * @project MultiThreadDownLoad
  10. * @blog http://blog.csdn.net/IBM_hoojo
  11. * @email hoojo_@126.com
  12. * @version 1.0
  13. */ 
  14. public class DownloadInfo { 
  15.     //下载文件url 
  16.     private String url; 
  17.     //下载文件名称 
  18.     private String fileName; 
  19.     //下载文件路径 
  20.     private String filePath; 
  21.     //分成多少段下载, 每一段用一个线程完成下载 
  22.     private int splitter; 
  23.      
  24.     //下载文件默认保存路径 
  25.     private final
    static String FILE_PATH = "C:/temp"; 
  26.     //默认分块数、线程数 
  27.     private final
    static int SPLITTER_NUM =
    5; 
  28.      
  29.     public DownloadInfo() { 
  30.         super(); 
  31.     } 
  32.      
  33.     /**
  34.      * @param url 下载地址
  35.      */ 
  36.     public DownloadInfo(String url) { 
  37.         this(url,
    null, null, SPLITTER_NUM); 
  38.     } 
  39.      
  40.     /**
  41.      * @param url 下载地址url
  42.      * @param splitter 分成多少段或是多少个线程下载
  43.      */ 
  44.     public DownloadInfo(String url,
    int splitter) { 
  45.         this(url,
    null, null, splitter); 
  46.     } 
  47.      
  48.     /***
  49.      * @param url 下载地址
  50.      * @param fileName 文件名称
  51.      * @param filePath 文件保存路径
  52.      * @param splitter 分成多少段或是多少个线程下载
  53.      */ 
  54.     public DownloadInfo(String url, String fileName, String filePath,
    int splitter) { 
  55.         super(); 
  56.         if (url == null ||
    "".equals(url)) { 
  57.             throw
    new RuntimeException("url is not null!"); 
  58.         } 
  59.         this.url =  url; 
  60.         this.fileName = (fileName ==
    null || "".equals(fileName)) ? getFileName(url) : fileName; 
  61.         this.filePath = (filePath ==
    null || "".equals(filePath)) ? FILE_PATH : filePath; 
  62.         this.splitter = (splitter <
    1) ? SPLITTER_NUM : splitter; 
  63.     } 
  64.      
  65.     /**
  66.      * <b>function:</b> 通过url获得文件名称
  67.      * @author hoojo
  68.      * @createDate 2011-9-30 下午05:00:00
  69.      * @param url
  70.      * @return
  71.      */ 
  72.     private String getFileName(String url) { 
  73.         return url.substring(url.lastIndexOf("/") +
    1, url.length()); 
  74.     } 
  75.      
  76.     public String getUrl() { 
  77.         return url; 
  78.     } 
  79.  
  80.     public void setUrl(String url) { 
  81.         if (url ==
    null || "".equals(url)) { 
  82.             throw new RuntimeException("url is not null!"); 
  83.         } 
  84.         this.url = url; 
  85.     } 
  86.  
  87.     public String getFileName() { 
  88.         return fileName; 
  89.     } 
  90.  
  91.     public void setFileName(String fileName) { 
  92.         this.fileName = (fileName ==
    null || "".equals(fileName)) ? getFileName(url) : fileName; 
  93.     } 
  94.  
  95.     public String getFilePath() { 
  96.         return filePath; 
  97.     } 
  98.  
  99.     public void setFilePath(String filePath) { 
  100.         this.filePath = (filePath ==
    null || "".equals(filePath)) ? FILE_PATH : filePath; 
  101.     } 
  102.  
  103.     public int getSplitter() { 
  104.         return splitter; 
  105.     } 
  106.  
  107.     public void setSplitter(int splitter) { 
  108.         this.splitter = (splitter <
    1) ? SPLITTER_NUM : splitter; 
  109.     } 
  110.      
  111.     @Override 
  112.     public String toString() { 
  113.         return this.url +
    "#" + this.fileName +
    "#" + this.filePath +
    "#" + this.splitter; 
  114.     } 
package com.hoo.entity;

/**
 * <b>function:</b> 下载文件信息类
 * @author hoojo
 * @createDate 2011-9-21 下午05:14:58
 * @file DownloadInfo.java
 * @package com.hoo.entity
 * @project MultiThreadDownLoad
 * @blog http://blog.csdn.net/IBM_hoojo
 * @email hoojo_@126.com
 * @version 1.0
 */
public class DownloadInfo {
	//下载文件url
	private String url;
	//下载文件名称
	private String fileName;
	//下载文件路径
	private String filePath;
	//分成多少段下载, 每一段用一个线程完成下载
	private int splitter;

	//下载文件默认保存路径
	private final static String FILE_PATH = "C:/temp";
	//默认分块数、线程数
	private final static int SPLITTER_NUM = 5;

	public DownloadInfo() {
		super();
	}

	/**
	 * @param url 下载地址
	 */
	public DownloadInfo(String url) {
		this(url, null, null, SPLITTER_NUM);
	}

	/**
	 * @param url 下载地址url
	 * @param splitter 分成多少段或是多少个线程下载
	 */
	public DownloadInfo(String url, int splitter) {
		this(url, null, null, splitter);
	}

	/***
	 * @param url 下载地址
	 * @param fileName 文件名称
	 * @param filePath 文件保存路径
	 * @param splitter 分成多少段或是多少个线程下载
	 */
	public DownloadInfo(String url, String fileName, String filePath, int splitter) {
		super();
		if (url == null || "".equals(url)) {
			throw new RuntimeException("url is not null!");
		}
		this.url =  url;
		this.fileName = (fileName == null || "".equals(fileName)) ? getFileName(url) : fileName;
		this.filePath = (filePath == null || "".equals(filePath)) ? FILE_PATH : filePath;
		this.splitter = (splitter < 1) ? SPLITTER_NUM : splitter;
	}

	/**
	 * <b>function:</b> 通过url获得文件名称
	 * @author hoojo
	 * @createDate 2011-9-30 下午05:00:00
	 * @param url
	 * @return
	 */
	private String getFileName(String url) {
		return url.substring(url.lastIndexOf("/") + 1, url.length());
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		if (url == null || "".equals(url)) {
			throw new RuntimeException("url is not null!");
		}
		this.url = url;
	}

	public String getFileName() {
		return fileName;
	}

	public void setFileName(String fileName) {
		this.fileName = (fileName == null || "".equals(fileName)) ? getFileName(url) : fileName;
	}

	public String getFilePath() {
		return filePath;
	}

	public void setFilePath(String filePath) {
		this.filePath = (filePath == null || "".equals(filePath)) ? FILE_PATH : filePath;
	}

	public int getSplitter() {
		return splitter;
	}

	public void setSplitter(int splitter) {
		this.splitter = (splitter < 1) ? SPLITTER_NUM : splitter;
	}

	@Override
	public String toString() {
		return this.url + "#" + this.fileName + "#" + this.filePath + "#" + this.splitter;
	}
}

 

二、随机写入一段文件

[java] view plaincopyprint?

  1. package com.hoo.download; 
  2.  
  3. import java.io.IOException; 
  4. import java.io.RandomAccessFile; 
  5.  
  6. /**
  7. * <b>function:</b> 写入文件、保存文件
  8. * @author hoojo
  9. * @createDate 2011-9-21 下午05:44:02
  10. * @file SaveItemFile.java
  11. * @package com.hoo.download
  12. * @project MultiThreadDownLoad
  13. * @blog http://blog.csdn.net/IBM_hoojo
  14. * @email hoojo_@126.com
  15. * @version 1.0
  16. */ 
  17. public class SaveItemFile { 
  18.     //存储文件 
  19.     private RandomAccessFile itemFile; 
  20.      
  21.     public SaveItemFile()
    throws IOException { 
  22.         this("",
    0); 
  23.     } 
  24.      
  25.     /**
  26.      * @param name 文件路径、名称
  27.      * @param pos 写入点位置 position
  28.      * @throws IOException
  29.      */ 
  30.     public SaveItemFile(String name,
    long pos) throws IOException { 
  31.         itemFile = new RandomAccessFile(name,
    "rw"); 
  32.         //在指定的pos位置开始写入数据 
  33.         itemFile.seek(pos); 
  34.     } 
  35.      
  36.     /**
  37.      * <b>function:</b> 同步方法写入文件
  38.      * @author hoojo
  39.      * @createDate 2011-9-26 下午12:21:22
  40.      * @param buff 缓冲数组
  41.      * @param start 起始位置
  42.      * @param length 长度
  43.      * @return
  44.      */ 
  45.     public synchronized
    int write(byte[] buff,
    int start, int length) { 
  46.         int i = -1; 
  47.         try { 
  48.             itemFile.write(buff, start, length); 
  49.             i = length; 
  50.         } catch (IOException e) { 
  51.             e.printStackTrace(); 
  52.         } 
  53.         return i; 
  54.     } 
  55.      
  56.     public void close()
    throws IOException { 
  57.         if (itemFile !=
    null) { 
  58.             itemFile.close(); 
  59.         } 
  60.     } 
package com.hoo.download;

import java.io.IOException;
import java.io.RandomAccessFile;

/**
 * <b>function:</b> 写入文件、保存文件
 * @author hoojo
 * @createDate 2011-9-21 下午05:44:02
 * @file SaveItemFile.java
 * @package com.hoo.download
 * @project MultiThreadDownLoad
 * @blog http://blog.csdn.net/IBM_hoojo
 * @email hoojo_@126.com
 * @version 1.0
 */
public class SaveItemFile {
	//存储文件
	private RandomAccessFile itemFile;

	public SaveItemFile() throws IOException {
		this("", 0);
	}

	/**
	 * @param name 文件路径、名称
	 * @param pos 写入点位置 position
	 * @throws IOException
	 */
	public SaveItemFile(String name, long pos) throws IOException {
		itemFile = new RandomAccessFile(name, "rw");
		//在指定的pos位置开始写入数据
		itemFile.seek(pos);
	}

	/**
	 * <b>function:</b> 同步方法写入文件
	 * @author hoojo
	 * @createDate 2011-9-26 下午12:21:22
	 * @param buff 缓冲数组
	 * @param start 起始位置
	 * @param length 长度
	 * @return
	 */
	public synchronized int write(byte[] buff, int start, int length) {
		int i = -1;
		try {
			itemFile.write(buff, start, length);
			i = length;
		} catch (IOException e) {
			e.printStackTrace();
		}
		return i;
	}

	public void close() throws IOException {
		if (itemFile != null) {
			itemFile.close();
		}
	}
}

这个类主要是完成向本地的指定文件指针出开始写入文件,并返回当前写入文件的长度(文件指针)。这个类将被线程调用,文件被分成对应的块后,将被线程调用。每个线程都将会调用这个类完成文件的随机写入。

三、单个线程下载文件

[java] view plaincopyprint?

  1. package com.hoo.download; 
  2.  
  3. import java.io.IOException; 
  4. import java.io.InputStream; 
  5. import java.net.HttpURLConnection; 
  6. import java.net.MalformedURLException; 
  7. import java.net.URL; 
  8. import java.net.URLConnection; 
  9. import com.hoo.util.LogUtils; 
  10.  
  11. /**
  12. * <b>function:</b> 单线程下载文件
  13. * @author hoojo
  14. * @createDate 2011-9-22 下午02:55:10
  15. * @file DownloadFile.java
  16. * @package com.hoo.download
  17. * @project MultiThreadDownLoad
  18. * @blog http://blog.csdn.net/IBM_hoojo
  19. * @email hoojo_@126.com
  20. * @version 1.0
  21. */ 
  22. public class DownloadFile
    extends Thread { 
  23.      
  24.     //下载文件url 
  25.     private String url; 
  26.     //下载文件起始位置   
  27.     private long startPos; 
  28.     //下载文件结束位置 
  29.     private long endPos; 
  30.     //线程id 
  31.     private int threadId; 
  32.      
  33.     //下载是否完成 
  34.     private boolean isDownloadOver =
    false; 
  35.  
  36.     private SaveItemFile itemFile; 
  37.      
  38.     private static
    final int BUFF_LENGTH =
    1024 * 8; 
  39.      
  40.     /**
  41.      * @param url 下载文件url
  42.      * @param name 文件名称
  43.      * @param startPos 下载文件起点
  44.      * @param endPos 下载文件结束点
  45.      * @param threadId 线程id
  46.      * @throws IOException
  47.      */ 
  48.     public DownloadFile(String url, String name,
    long startPos, long endPos,
    int threadId) throws IOException { 
  49.         super(); 
  50.         this.url = url; 
  51.         this.startPos = startPos; 
  52.         this.endPos = endPos; 
  53.         this.threadId = threadId; 
  54.         //分块下载写入文件内容 
  55.         this.itemFile =
    new SaveItemFile(name, startPos); 
  56.     } 
  57.  
  58.      
  59.     @Override 
  60.     public void run() { 
  61.         while (endPos > startPos && !isDownloadOver) { 
  62.             try { 
  63.                 URL url = new URL(this.url); 
  64.                 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 
  65.                  
  66.                 // 设置连接超时时间为10000ms 
  67.                 conn.setConnectTimeout(10000); 
  68.                 // 设置读取数据超时时间为10000ms 
  69.                 conn.setReadTimeout(10000); 
  70.                  
  71.                 setHeader(conn); 
  72.                  
  73.                 String property = "bytes=" + startPos +
    "-"; 
  74.                 conn.setRequestProperty("RANGE", property); 
  75.                  
  76.                 //输出log信息 
  77.                 LogUtils.log("开始 " + threadId +
    ":" + property + endPos); 
  78.                 //printHeader(conn); 
  79.                  
  80.                 //获取文件输入流,读取文件内容 
  81.                 InputStream is = conn.getInputStream(); 
  82.                  
  83.                 byte[] buff =
    new byte[BUFF_LENGTH]; 
  84.                 int length = -1; 
  85.                 LogUtils.log("#start#Thread: " + threadId +
    ", startPos: " + startPos + ", endPos: " + endPos); 
  86.                 while ((length = is.read(buff)) >
    0 && startPos < endPos && !isDownloadOver) { 
  87.                     //写入文件内容,返回最后写入的长度 
  88.                     startPos += itemFile.write(buff,
    0, length); 
  89.                 } 
  90.                 LogUtils.log("#over#Thread: " + threadId +
    ", startPos: " + startPos + ", endPos: " + endPos); 
  91.                 LogUtils.log("Thread " + threadId +
    " is execute over!"); 
  92.                 this.isDownloadOver =
    true; 
  93.             } catch (MalformedURLException e) { 
  94.                 e.printStackTrace(); 
  95.             } catch (IOException e) { 
  96.                 e.printStackTrace(); 
  97.             } finally { 
  98.                 try { 
  99.                     if (itemFile !=
    null) { 
  100.                         itemFile.close(); 
  101.                     } 
  102.                 } catch (IOException e) { 
  103.                     e.printStackTrace(); 
  104.                 } 
  105.             } 
  106.         } 
  107.         if (endPos < startPos && !isDownloadOver) { 
  108.             LogUtils.log("Thread " + threadId  +
    " startPos > endPos, not need download file !"); 
  109.             this.isDownloadOver =
    true; 
  110.         } 
  111.         if (endPos == startPos && !isDownloadOver) { 
  112.             LogUtils.log("Thread " + threadId  +
    " startPos = endPos, not need download file !"); 
  113.             this.isDownloadOver =
    true; 
  114.         } 
  115.     } 
  116.      
  117.     /**
  118.      * <b>function:</b> 打印下载文件头部信息
  119.      * @author hoojo
  120.      * @createDate 2011-9-22 下午05:44:35
  121.      * @param conn HttpURLConnection
  122.      */ 
  123.     public static
    void printHeader(URLConnection conn) { 
  124.         int i = 1; 
  125.         while (true) { 
  126.             String header = conn.getHeaderFieldKey(i); 
  127.             i++; 
  128.             if (header != null) { 
  129.                 LogUtils.info(header +
    ":" + conn.getHeaderField(i)); 
  130.             } else { 
  131.                 break; 
  132.             } 
  133.         } 
  134.     } 
  135.      
  136.     /**
  137.      * <b>function:</b> 设置URLConnection的头部信息,伪装请求信息
  138.      * @author hoojo
  139.      * @createDate 2011-9-28 下午05:29:43
  140.      * @param con
  141.      */ 
  142.     public static
    void setHeader(URLConnection conn) { 
  143.         conn.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"); 
  144.         conn.setRequestProperty("Accept-Language",
    "en-us,en;q=0.7,zh-cn;q=0.3"); 
  145.         conn.setRequestProperty("Accept-Encoding",
    "utf-8"); 
  146.         conn.setRequestProperty("Accept-Charset",
    "ISO-8859-1,utf-8;q=0.7,*;q=0.7"); 
  147.         conn.setRequestProperty("Keep-Alive",
    "300"); 
  148.         conn.setRequestProperty("connnection",
    "keep-alive"); 
  149.         conn.setRequestProperty("If-Modified-Since",
    "Fri, 02 Jan 2009 17:00:05 GMT"); 
  150.         conn.setRequestProperty("If-None-Match",
    "\"1261d8-4290-df64d224\""); 
  151.         conn.setRequestProperty("Cache-conntrol",
    "max-age=0"); 
  152.         conn.setRequestProperty("Referer",
    "http://www.baidu.com"); 
  153.     } 
  154.      
  155.     public boolean isDownloadOver() { 
  156.         return isDownloadOver; 
  157.     } 
  158.      
  159.     public long getStartPos() { 
  160.         return startPos; 
  161.     } 
  162.  
  163.     public long getEndPos() { 
  164.         return endPos; 
  165.     } 
package com.hoo.download;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import com.hoo.util.LogUtils;

/**
 * <b>function:</b> 单线程下载文件
 * @author hoojo
 * @createDate 2011-9-22 下午02:55:10
 * @file DownloadFile.java
 * @package com.hoo.download
 * @project MultiThreadDownLoad
 * @blog http://blog.csdn.net/IBM_hoojo
 * @email hoojo_@126.com
 * @version 1.0
 */
public class DownloadFile extends Thread {

	//下载文件url
	private String url;
	//下载文件起始位置
	private long startPos;
	//下载文件结束位置
	private long endPos;
	//线程id
	private int threadId;

	//下载是否完成
	private boolean isDownloadOver = false;

	private SaveItemFile itemFile;

	private static final int BUFF_LENGTH = 1024 * 8;

	/**
	 * @param url 下载文件url
	 * @param name 文件名称
	 * @param startPos 下载文件起点
	 * @param endPos 下载文件结束点
	 * @param threadId 线程id
	 * @throws IOException
	 */
	public DownloadFile(String url, String name, long startPos, long endPos, int threadId) throws IOException {
		super();
		this.url = url;
		this.startPos = startPos;
		this.endPos = endPos;
		this.threadId = threadId;
		//分块下载写入文件内容
		this.itemFile = new SaveItemFile(name, startPos);
	}

	@Override
	public void run() {
		while (endPos > startPos && !isDownloadOver) {
			try {
				URL url = new URL(this.url);
				HttpURLConnection conn = (HttpURLConnection) url.openConnection();

				// 设置连接超时时间为10000ms
				conn.setConnectTimeout(10000);
				// 设置读取数据超时时间为10000ms
				conn.setReadTimeout(10000);

				setHeader(conn);

				String property = "bytes=" + startPos + "-";
				conn.setRequestProperty("RANGE", property);

				//输出log信息
				LogUtils.log("开始 " + threadId + ":" + property + endPos);
				//printHeader(conn);

				//获取文件输入流,读取文件内容
				InputStream is = conn.getInputStream();

				byte[] buff = new byte[BUFF_LENGTH];
				int length = -1;
				LogUtils.log("#start#Thread: " + threadId + ", startPos: " + startPos + ", endPos: " + endPos);
				while ((length = is.read(buff)) > 0 && startPos < endPos && !isDownloadOver) {
					//写入文件内容,返回最后写入的长度
					startPos += itemFile.write(buff, 0, length);
				}
				LogUtils.log("#over#Thread: " + threadId + ", startPos: " + startPos + ", endPos: " + endPos);
				LogUtils.log("Thread " + threadId + " is execute over!");
				this.isDownloadOver = true;
			} catch (MalformedURLException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			} finally {
				try {
					if (itemFile != null) {
						itemFile.close();
					}
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		if (endPos < startPos && !isDownloadOver) {
			LogUtils.log("Thread " + threadId  + " startPos > endPos, not need download file !");
			this.isDownloadOver = true;
		}
		if (endPos == startPos && !isDownloadOver) {
			LogUtils.log("Thread " + threadId  + " startPos = endPos, not need download file !");
			this.isDownloadOver = true;
		}
	}

	/**
	 * <b>function:</b> 打印下载文件头部信息
	 * @author hoojo
	 * @createDate 2011-9-22 下午05:44:35
	 * @param conn HttpURLConnection
	 */
	public static void printHeader(URLConnection conn) {
		int i = 1;
		while (true) {
			String header = conn.getHeaderFieldKey(i);
			i++;
			if (header != null) {
				LogUtils.info(header + ":" + conn.getHeaderField(i));
			} else {
				break;
			}
		}
	}

	/**
	 * <b>function:</b> 设置URLConnection的头部信息,伪装请求信息
	 * @author hoojo
	 * @createDate 2011-9-28 下午05:29:43
	 * @param con
	 */
	public static void setHeader(URLConnection conn) {
		conn.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");
		conn.setRequestProperty("Accept-Language", "en-us,en;q=0.7,zh-cn;q=0.3");
		conn.setRequestProperty("Accept-Encoding", "utf-8");
		conn.setRequestProperty("Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7");
		conn.setRequestProperty("Keep-Alive", "300");
		conn.setRequestProperty("connnection", "keep-alive");
		conn.setRequestProperty("If-Modified-Since", "Fri, 02 Jan 2009 17:00:05 GMT");
		conn.setRequestProperty("If-None-Match", "\"1261d8-4290-df64d224\"");
		conn.setRequestProperty("Cache-conntrol", "max-age=0");
		conn.setRequestProperty("Referer", "http://www.baidu.com");
	}

	public boolean isDownloadOver() {
		return isDownloadOver;
	}

	public long getStartPos() {
		return startPos;
	}

	public long getEndPos() {
		return endPos;
	}
}

这个类主要是完成单个线程的文件下载,将通过URLConnection读取指定url的资源信息。然后用InputStream读取文件内容,然后调用调用SaveItemFile类,向本地写入当前要读取的块的内容。

 

四、分段多线程写入文件内容

[java] view plaincopyprint?

  1. package com.hoo.download; 
  2.  
  3. import java.io.DataInputStream; 
  4. import java.io.DataOutputStream; 
  5. import java.io.File; 
  6. import java.io.FileInputStream; 
  7. import java.io.FileOutputStream; 
  8. import java.io.IOException; 
  9. import java.net.HttpURLConnection; 
  10. import java.net.MalformedURLException; 
  11. import java.net.URL; 
  12. import com.hoo.entity.DownloadInfo; 
  13. import com.hoo.util.LogUtils; 
  14.  
  15. /**
  16. * <b>function:</b> 分批量下载文件
  17. * @author hoojo
  18. * @createDate 2011-9-22 下午05:51:54
  19. * @file BatchDownloadFile.java
  20. * @package com.hoo.download
  21. * @project MultiThreadDownLoad
  22. * @blog http://blog.csdn.net/IBM_hoojo
  23. * @email hoojo_@126.com
  24. * @version 1.0
  25. */ 
  26. public class BatchDownloadFile
    implements Runnable { 
  27.     //下载文件信息  
  28.     private DownloadInfo downloadInfo; 
  29.     //一组开始下载位置 
  30.     private long[] startPos; 
  31.     //一组结束下载位置 
  32.     private long[] endPos; 
  33.     //休眠时间 
  34.     private static
    final int SLEEP_SECONDS =
    500; 
  35.     //子线程下载 
  36.     private DownloadFile[] fileItem; 
  37.     //文件长度 
  38.     private int length; 
  39.     //是否第一个文件 
  40.     private boolean first =
    true; 
  41.     //是否停止下载 
  42.     private boolean stop =
    false; 
  43.     //临时文件信息 
  44.     private File tempFile; 
  45.      
  46.     public BatchDownloadFile(DownloadInfo downloadInfo) { 
  47.         this.downloadInfo = downloadInfo; 
  48.         String tempPath = this.downloadInfo.getFilePath() + File.separator + downloadInfo.getFileName() +
    ".position"; 
  49.         tempFile = new File(tempPath); 
  50.         //如果存在读入点位置的文件 
  51.         if (tempFile.exists()) { 
  52.             first = false; 
  53.             //就直接读取内容 
  54.             try { 
  55.                 readPosInfo(); 
  56.             } catch (IOException e) { 
  57.                 e.printStackTrace(); 
  58.             } 
  59.         } else { 
  60.             //数组的长度就要分成多少段的数量 
  61.             startPos = new
    long[downloadInfo.getSplitter()]; 
  62.             endPos = new long[downloadInfo.getSplitter()]; 
  63.         } 
  64.     } 
  65.      
  66.     @Override 
  67.     public void run() { 
  68.         //首次下载,获取下载文件长度 
  69.         if (first) { 
  70.             length = this.getFileSize();//获取文件长度 
  71.             if (length == -1) { 
  72.                 LogUtils.log("file length is know!"); 
  73.                 stop = true; 
  74.             } else if (length == -2) { 
  75.                 LogUtils.log("read file length is error!"); 
  76.                 stop = true; 
  77.             } else
    if (length > 0) { 
  78.                 /**
  79.                  * eg
  80.                  * start: 1, 3, 5, 7, 9
  81.                  * end: 3, 5, 7, 9, length
  82.                  */ 
  83.                 for (int i =
    0, len = startPos.length; i < len; i++) { 
  84.                     int size = i * (length / len); 
  85.                     startPos[i] = size; 
  86.                      
  87.                     //设置最后一个结束点的位置 
  88.                     if (i == len -
    1) { 
  89.                         endPos[i] = length; 
  90.                     } else { 
  91.                         size = (i + 1) * (length / len); 
  92.                         endPos[i] = size; 
  93.                     } 
  94.                     LogUtils.log("start-end Position[" + i +
    "]: " + startPos[i] + "-" + endPos[i]); 
  95.                 } 
  96.             } else { 
  97.                 LogUtils.log("get file length is error, download is stop!"); 
  98.                 stop = true; 
  99.             } 
  100.         } 
  101.          
  102.         //子线程开始下载 
  103.         if (!stop) { 
  104.             //创建单线程下载对象数组 
  105.             fileItem = new DownloadFile[startPos.length];//startPos.length = downloadInfo.getSplitter() 
  106.             for (int i =
    0; i < startPos.length; i++) { 
  107.                 try { 
  108.                     //创建指定个数单线程下载对象,每个线程独立完成指定块内容的下载 
  109.                     fileItem[i] = new DownloadFile( 
  110.                         downloadInfo.getUrl(),  
  111.                         this.downloadInfo.getFilePath() + File.separator + downloadInfo.getFileName(),  
  112.                         startPos[i], endPos[i], i 
  113.                     ); 
  114.                     fileItem[i].start();//启动线程,开始下载 
  115.                     LogUtils.log("Thread: " + i +
    ", startPos: " + startPos[i] + ", endPos: " + endPos[i]); 
  116.                 } catch (IOException e) { 
  117.                     e.printStackTrace(); 
  118.                 } 
  119.             } 
  120.              
  121.             //循环写入下载文件长度信息 
  122.             while (!stop) { 
  123.                 try { 
  124.                     writePosInfo(); 
  125.                     LogUtils.log("downloading……"); 
  126.                     Thread.sleep(SLEEP_SECONDS); 
  127.                     stop = true; 
  128.                 } catch (IOException e) { 
  129.                     e.printStackTrace(); 
  130.                 } catch (InterruptedException e) { 
  131.                     e.printStackTrace(); 
  132.                 } 
  133.                 for (int i =
    0; i < startPos.length; i++) { 
  134.                     if (!fileItem[i].isDownloadOver()) { 
  135.                         stop = false; 
  136.                         break; 
  137.                     } 
  138.                 } 
  139.             } 
  140.             LogUtils.info("Download task is finished!"); 
  141.         } 
  142.     } 
  143.      
  144.     /**
  145.      * 将写入点数据保存在临时文件中
  146.      * @author hoojo
  147.      * @createDate 2011-9-23 下午05:25:37
  148.      * @throws IOException
  149.      */ 
  150.     private void writePosInfo()
    throws IOException { 
  151.         DataOutputStream dos = new DataOutputStream(new FileOutputStream(tempFile)); 
  152.         dos.writeInt(startPos.length); 
  153.         for (int i =
    0; i < startPos.length; i++) { 
  154.             dos.writeLong(fileItem[i].getStartPos()); 
  155.             dos.writeLong(fileItem[i].getEndPos()); 
  156.             //LogUtils.info("[" + fileItem[i].getStartPos() + "#" + fileItem[i].getEndPos() + "]"); 
  157.         } 
  158.         dos.close(); 
  159.     } 
  160.      
  161.     /**
  162.      * <b>function:</b>读取写入点的位置信息
  163.      * @author hoojo
  164.      * @createDate 2011-9-23 下午05:30:29
  165.      * @throws IOException
  166.      */ 
  167.     private void readPosInfo()
    throws IOException { 
  168.         DataInputStream dis = new DataInputStream(new FileInputStream(tempFile)); 
  169.         int startPosLength = dis.readInt(); 
  170.         startPos = new long[startPosLength]; 
  171.         endPos = new
    long[startPosLength]; 
  172.         for (int i =
    0; i < startPosLength; i++) { 
  173.             startPos[i] = dis.readLong(); 
  174.             endPos[i] = dis.readLong(); 
  175.         } 
  176.         dis.close(); 
  177.     } 
  178.      
  179.     /**
  180.      * <b>function:</b> 获取下载文件的长度
  181.      * @author hoojo
  182.      * @createDate 2011-9-26 下午12:15:08
  183.      * @return
  184.      */ 
  185.     private int getFileSize() { 
  186.         int fileLength = -1; 
  187.         try { 
  188.             URL url = new URL(this.downloadInfo.getUrl()); 
  189.             HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 
  190.              
  191.             DownloadFile.setHeader(conn); 
  192.  
  193.             int stateCode = conn.getResponseCode(); 
  194.             //判断http status是否为HTTP/1.1 206 Partial Content或者200 OK 
  195.             if (stateCode != HttpURLConnection.HTTP_OK && stateCode != HttpURLConnection.HTTP_PARTIAL) { 
  196.                 LogUtils.log("Error Code: " + stateCode); 
  197.                 return -2; 
  198.             } else if (stateCode >=
    400) { 
  199.                 LogUtils.log("Error Code: " + stateCode); 
  200.                 return -2; 
  201.             } else { 
  202.                 //获取长度 
  203.                 fileLength = conn.getContentLength(); 
  204.                 LogUtils.log("FileLength: " + fileLength); 
  205.             } 
  206.              
  207.             //读取文件长度 
  208.             /*for (int i = 1; ; i++) {
  209.                 String header = conn.getHeaderFieldKey(i);
  210.                 if (header != null) {
  211.                     if ("Content-Length".equals(header)) {
  212.                         fileLength = Integer.parseInt(conn.getHeaderField(i));
  213.                         break;
  214.                     }
  215.                 } else {
  216.                     break;
  217.                 }
  218.             }
  219.             */ 
  220.              
  221.             DownloadFile.printHeader(conn); 
  222.         } catch (MalformedURLException e) { 
  223.             e.printStackTrace(); 
  224.         } catch (IOException e) { 
  225.             e.printStackTrace(); 
  226.         } 
  227.         return fileLength; 
  228.     } 
package com.hoo.download;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import com.hoo.entity.DownloadInfo;
import com.hoo.util.LogUtils;

/**
 * <b>function:</b> 分批量下载文件
 * @author hoojo
 * @createDate 2011-9-22 下午05:51:54
 * @file BatchDownloadFile.java
 * @package com.hoo.download
 * @project MultiThreadDownLoad
 * @blog http://blog.csdn.net/IBM_hoojo
 * @email hoojo_@126.com
 * @version 1.0
 */
public class BatchDownloadFile implements Runnable {
	//下载文件信息
	private DownloadInfo downloadInfo;
	//一组开始下载位置
	private long[] startPos;
	//一组结束下载位置
	private long[] endPos;
	//休眠时间
	private static final int SLEEP_SECONDS = 500;
	//子线程下载
	private DownloadFile[] fileItem;
	//文件长度
	private int length;
	//是否第一个文件
	private boolean first = true;
	//是否停止下载
	private boolean stop = false;
	//临时文件信息
	private File tempFile;

	public BatchDownloadFile(DownloadInfo downloadInfo) {
		this.downloadInfo = downloadInfo;
		String tempPath = this.downloadInfo.getFilePath() + File.separator + downloadInfo.getFileName() + ".position";
		tempFile = new File(tempPath);
		//如果存在读入点位置的文件
		if (tempFile.exists()) {
			first = false;
			//就直接读取内容
			try {
				readPosInfo();
			} catch (IOException e) {
				e.printStackTrace();
			}
		} else {
			//数组的长度就要分成多少段的数量
			startPos = new long[downloadInfo.getSplitter()];
			endPos = new long[downloadInfo.getSplitter()];
		}
	}

	@Override
	public void run() {
		//首次下载,获取下载文件长度
		if (first) {
			length = this.getFileSize();//获取文件长度
			if (length == -1) {
				LogUtils.log("file length is know!");
				stop = true;
			} else if (length == -2) {
				LogUtils.log("read file length is error!");
				stop = true;
			} else if (length > 0) {
				/**
				 * eg
				 * start: 1, 3, 5, 7, 9
				 * end: 3, 5, 7, 9, length
				 */
				for (int i = 0, len = startPos.length; i < len; i++) {
					int size = i * (length / len);
					startPos[i] = size;

					//设置最后一个结束点的位置
					if (i == len - 1) {
						endPos[i] = length;
					} else {
						size = (i + 1) * (length / len);
						endPos[i] = size;
					}
					LogUtils.log("start-end Position[" + i + "]: " + startPos[i] + "-" + endPos[i]);
				}
			} else {
				LogUtils.log("get file length is error, download is stop!");
				stop = true;
			}
		}

		//子线程开始下载
		if (!stop) {
			//创建单线程下载对象数组
			fileItem = new DownloadFile[startPos.length];//startPos.length = downloadInfo.getSplitter()
			for (int i = 0; i < startPos.length; i++) {
				try {
					//创建指定个数单线程下载对象,每个线程独立完成指定块内容的下载
					fileItem[i] = new DownloadFile(
						downloadInfo.getUrl(),
						this.downloadInfo.getFilePath() + File.separator + downloadInfo.getFileName(),
						startPos[i], endPos[i], i
					);
					fileItem[i].start();//启动线程,开始下载
					LogUtils.log("Thread: " + i + ", startPos: " + startPos[i] + ", endPos: " + endPos[i]);
				} catch (IOException e) {
					e.printStackTrace();
				}
			}

			//循环写入下载文件长度信息
			while (!stop) {
				try {
					writePosInfo();
					LogUtils.log("downloading……");
					Thread.sleep(SLEEP_SECONDS);
					stop = true;
				} catch (IOException e) {
					e.printStackTrace();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				for (int i = 0; i < startPos.length; i++) {
					if (!fileItem[i].isDownloadOver()) {
						stop = false;
						break;
					}
				}
			}
			LogUtils.info("Download task is finished!");
		}
	}

	/**
	 * 将写入点数据保存在临时文件中
	 * @author hoojo
	 * @createDate 2011-9-23 下午05:25:37
	 * @throws IOException
	 */
	private void writePosInfo() throws IOException {
		DataOutputStream dos = new DataOutputStream(new FileOutputStream(tempFile));
		dos.writeInt(startPos.length);
		for (int i = 0; i < startPos.length; i++) {
			dos.writeLong(fileItem[i].getStartPos());
			dos.writeLong(fileItem[i].getEndPos());
			//LogUtils.info("[" + fileItem[i].getStartPos() + "#" + fileItem[i].getEndPos() + "]");
		}
		dos.close();
	}

	/**
	 * <b>function:</b>读取写入点的位置信息
	 * @author hoojo
	 * @createDate 2011-9-23 下午05:30:29
	 * @throws IOException
	 */
	private void readPosInfo() throws IOException {
		DataInputStream dis = new DataInputStream(new FileInputStream(tempFile));
		int startPosLength = dis.readInt();
		startPos = new long[startPosLength];
		endPos = new long[startPosLength];
		for (int i = 0; i < startPosLength; i++) {
			startPos[i] = dis.readLong();
			endPos[i] = dis.readLong();
		}
		dis.close();
	}

	/**
	 * <b>function:</b> 获取下载文件的长度
	 * @author hoojo
	 * @createDate 2011-9-26 下午12:15:08
	 * @return
	 */
	private int getFileSize() {
		int fileLength = -1;
		try {
			URL url = new URL(this.downloadInfo.getUrl());
			HttpURLConnection conn = (HttpURLConnection) url.openConnection();

			DownloadFile.setHeader(conn);

			int stateCode = conn.getResponseCode();
			//判断http status是否为HTTP/1.1 206 Partial Content或者200 OK
			if (stateCode != HttpURLConnection.HTTP_OK && stateCode != HttpURLConnection.HTTP_PARTIAL) {
				LogUtils.log("Error Code: " + stateCode);
				return -2;
			} else if (stateCode >= 400) {
				LogUtils.log("Error Code: " + stateCode);
				return -2;
			} else {
				//获取长度
				fileLength = conn.getContentLength();
				LogUtils.log("FileLength: " + fileLength);
			}

			//读取文件长度
			/*for (int i = 1; ; i++) {
				String header = conn.getHeaderFieldKey(i);
				if (header != null) {
					if ("Content-Length".equals(header)) {
						fileLength = Integer.parseInt(conn.getHeaderField(i));
						break;
					}
				} else {
					break;
				}
			}
			*/

			DownloadFile.printHeader(conn);
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return fileLength;
	}
}

这个类主要是完成读取指定url资源的内容,获取该资源的长度。然后将该资源分成指定的块数,将每块的起始下载位置、结束下载位置,分别保存在一个数组中。每块都单独开辟一个独立线程开始下载。在开始下载之前,需要创建一个临时文件,写入当前下载线程的开始下载指针位置和结束下载指针位置。

 

五、工具类、测试类

日志工具类

[java] view plaincopyprint?

  1. package com.hoo.util; 
  2.  
  3. /**
  4. * <b>function:</b> 日志工具类
  5. * @author hoojo
  6. * @createDate 2011-9-21 下午05:21:27
  7. * @file LogUtils.java
  8. * @package com.hoo.util
  9. * @project MultiThreadDownLoad
  10. * @blog http://blog.csdn.net/IBM_hoojo
  11. * @email hoojo_@126.com
  12. * @version 1.0
  13. */ 
  14. public abstract
    class LogUtils { 
  15.      
  16.     public static
    void log(Object message) { 
  17.         System.err.println(message); 
  18.     } 
  19.      
  20.     public static
    void log(String message) { 
  21.         System.err.println(message); 
  22.     } 
  23.      
  24.     public static
    void log(int message) { 
  25.         System.err.println(message); 
  26.     } 
  27.      
  28.     public static
    void info(Object message) { 
  29.         System.out.println(message); 
  30.     } 
  31.      
  32.     public static
    void info(String message) { 
  33.         System.out.println(message); 
  34.     } 
  35.      
  36.     public static
    void info(int message) { 
  37.         System.out.println(message); 
  38.     } 
package com.hoo.util;

/**
 * <b>function:</b> 日志工具类
 * @author hoojo
 * @createDate 2011-9-21 下午05:21:27
 * @file LogUtils.java
 * @package com.hoo.util
 * @project MultiThreadDownLoad
 * @blog http://blog.csdn.net/IBM_hoojo
 * @email hoojo_@126.com
 * @version 1.0
 */
public abstract class LogUtils {

	public static void log(Object message) {
		System.err.println(message);
	}

	public static void log(String message) {
		System.err.println(message);
	}

	public static void log(int message) {
		System.err.println(message);
	}

	public static void info(Object message) {
		System.out.println(message);
	}

	public static void info(String message) {
		System.out.println(message);
	}

	public static void info(int message) {
		System.out.println(message);
	}
}

下载工具类

[java] view plaincopyprint?

  1. package com.hoo.util; 
  2.  
  3. import com.hoo.download.BatchDownloadFile; 
  4. import com.hoo.entity.DownloadInfo; 
  5.  
  6. /**
  7. * <b>function:</b> 分块多线程下载工具类
  8. * @author hoojo
  9. * @createDate 2011-9-28 下午05:22:18
  10. * @file DownloadUtils.java
  11. * @package com.hoo.util
  12. * @project MultiThreadDownLoad
  13. * @blog http://blog.csdn.net/IBM_hoojo
  14. * @email hoojo_@126.com
  15. * @version 1.0
  16. */ 
  17. public abstract
    class DownloadUtils { 
  18.  
  19.     public static
    void download(String url) { 
  20.         DownloadInfo bean = new DownloadInfo(url); 
  21.         LogUtils.info(bean); 
  22.         BatchDownloadFile down = new BatchDownloadFile(bean); 
  23.         new Thread(down).start(); 
  24.     } 
  25.      
  26.     public static
    void download(String url, int threadNum) { 
  27.         DownloadInfo bean = new DownloadInfo(url, threadNum); 
  28.         LogUtils.info(bean); 
  29.         BatchDownloadFile down = new BatchDownloadFile(bean); 
  30.         new Thread(down).start(); 
  31.     } 
  32.      
  33.     public static
    void download(String url, String fileName, String filePath,
    int threadNum) { 
  34.         DownloadInfo bean = new DownloadInfo(url, fileName, filePath, threadNum); 
  35.         LogUtils.info(bean); 
  36.         BatchDownloadFile down = new BatchDownloadFile(bean); 
  37.         new Thread(down).start(); 
  38.     } 
package com.hoo.util;

import com.hoo.download.BatchDownloadFile;
import com.hoo.entity.DownloadInfo;

/**
 * <b>function:</b> 分块多线程下载工具类
 * @author hoojo
 * @createDate 2011-9-28 下午05:22:18
 * @file DownloadUtils.java
 * @package com.hoo.util
 * @project MultiThreadDownLoad
 * @blog http://blog.csdn.net/IBM_hoojo
 * @email hoojo_@126.com
 * @version 1.0
 */
public abstract class DownloadUtils {

	public static void download(String url) {
		DownloadInfo bean = new DownloadInfo(url);
		LogUtils.info(bean);
		BatchDownloadFile down = new BatchDownloadFile(bean);
		new Thread(down).start();
	}

	public static void download(String url, int threadNum) {
		DownloadInfo bean = new DownloadInfo(url, threadNum);
		LogUtils.info(bean);
		BatchDownloadFile down = new BatchDownloadFile(bean);
		new Thread(down).start();
	}

	public static void download(String url, String fileName, String filePath, int threadNum) {
		DownloadInfo bean = new DownloadInfo(url, fileName, filePath, threadNum);
		LogUtils.info(bean);
		BatchDownloadFile down = new BatchDownloadFile(bean);
		new Thread(down).start();
	}
}

下载测试类

[java] view plaincopyprint?

  1. package com.hoo.test; 
  2.  
  3. import com.hoo.util.DownloadUtils; 
  4.  
  5. /**
  6. * <b>function:</b> 下载测试
  7. * @author hoojo
  8. * @createDate 2011-9-23 下午05:49:46
  9. * @file TestDownloadMain.java
  10. * @package com.hoo.download
  11. * @project MultiThreadDownLoad
  12. * @blog http://blog.csdn.net/IBM_hoojo
  13. * @email hoojo_@126.com
  14. * @version 1.0
  15. */ 
  16. public class TestDownloadMain { 
  17.  
  18.     public static
    void main(String[] args) { 
  19.         /*DownloadInfo bean = new DownloadInfo("http://i7.meishichina.com/Health/UploadFiles/201109/2011092116224363.jpg");
  20.         System.out.println(bean);
  21.         BatchDownloadFile down = new BatchDownloadFile(bean);
  22.         new Thread(down).start();*/ 
  23.          
  24.         //DownloadUtils.download("http://i7.meishichina.com/Health/UploadFiles/201109/2011092116224363.jpg"); 
  25.         DownloadUtils.download("http://mp3.baidu.com/j?j=2&url=http%3A%2F%2Fzhangmenshiting2.baidu.com%2Fdata%2Fmusic%2F1669425%2F%25E9%2599%25B7%25E5%2585%25A5%25E7%2588%25B1%25E9%2587%258C%25E9%259D%25A2.mp3%3Fxcode%3D2ff36fb70737c816553396c56deab3f1",
    "aa.mp3", "c:/temp",
    5); 
  26.     } 
package com.hoo.test;

import com.hoo.util.DownloadUtils;

/**
 * <b>function:</b> 下载测试
 * @author hoojo
 * @createDate 2011-9-23 下午05:49:46
 * @file TestDownloadMain.java
 * @package com.hoo.download
 * @project MultiThreadDownLoad
 * @blog http://blog.csdn.net/IBM_hoojo
 * @email hoojo_@126.com
 * @version 1.0
 */
public class TestDownloadMain {

	public static void main(String[] args) {
		/*DownloadInfo bean = new DownloadInfo("http://i7.meishichina.com/Health/UploadFiles/201109/2011092116224363.jpg");
		System.out.println(bean);
		BatchDownloadFile down = new BatchDownloadFile(bean);
		new Thread(down).start();*/

		//DownloadUtils.download("http://i7.meishichina.com/Health/UploadFiles/201109/2011092116224363.jpg");
		DownloadUtils.download("http://mp3.baidu.com/j?j=2&url=http%3A%2F%2Fzhangmenshiting2.baidu.com%2Fdata%2Fmusic%2F1669425%2F%25E9%2599%25B7%25E5%2585%25A5%25E7%2588%25B1%25E9%2587%258C%25E9%259D%25A2.mp3%3Fxcode%3D2ff36fb70737c816553396c56deab3f1", "aa.mp3", "c:/temp", 5);
	}
}

多线程下载主要在第三部和第四部,其他的地方还是很好理解。源码中提供相应的注释了,便于理解。

本文转载于:http://blog.csdn.net/ibm_hoojo/article/details/6838222

时间: 2024-10-31 15:01:24

Java 多线程断点下载文件_详解的相关文章

Android多线程断点下载完整示例详解

MainActivity如下: package cc.activity; import java.io.File; import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.Message; import android.view.

使用技巧:对Java编程中的文件操作详解

编程|技巧|详解 一.获得控制台用户输入的信息 /** *//**获得控制台用户输入的信息 * @return * @throws IOException */ public String getInputMessage() throws IOException...{ System.out.println("请输入您的命令∶"); byte buffer[]=new byte[1024]; int count=System.in.read(buffer); char[] ch=new

使用java实现http多线程断点下载文件(二)_java

下载工具我想没有几个人不会用的吧,前段时间比较无聊,花了点时间用java写了个简单的http多线程下载程序,纯粹是无聊才写的,只实现了几个简单的功能,而且也没写界面,今天正好也是一个无聊日,就拿来写篇文章,班门弄斧一下,觉得好给个掌声,不好也不要喷,谢谢! 我实现的这个http下载工具功能很简单,就是一个多线程以及一个断点恢复,当然下载是必不可少的.那么大概先整理一下要做的事情: 1.连接资源服务器,获取资源信息,创建文件 2.切分资源,多线程下载 3.断点恢复功能 4.下载速率统计 大概就这几

使用java实现http多线程断点下载文件(一)_java

基本原理:利用URLConnection获取要下载文件的长度.头部等相关信息,并设置响应的头部信息.并且通过URLConnection获取输入流,将文件分成指定的块,每一块单独开辟一个线程完成数据的读取.写入.通过输入流读取下载文件的信息,然后将读取的信息用RandomAccessFile随机写入到本地文件中.同时,每个线程写入的数据都文件指针也就是写入数据的长度,需要保存在一个临时文件中.这样当本次下载没有完成的时候,下次下载的时候就从这个文件中读取上一次下载的文件长度,然后继续接着上一次的位

java多线程编程之Synchronized关键字详解_java

本文介绍JAVA多线程中的synchronized关键字作为对象锁的一些知识点. 所谓对象锁,就是就是synchronized 给某个对象 加锁.关于 对象锁 可参考:这篇文章  一.分析 synchronized可以修饰实例方法,如下形式: public class MyObject { synchronized public void methodA() { //do something.... } 这里,synchronized 关键字锁住的是当前对象.这也是称为对象锁的原因. 为啥锁住当

百度云Windows版更改下载文件路径详解分享

给各位百度云软件的使用者们来详细的解析分享一下百度云Windows版更改下载文件路径的方法. 方法分享: 百度云Windows版更改下载文件的路径方法:点击账号右边的设置,进入[设置]界面,点击[高级]即可更改您下载文件路径.   好了,以上的信息就是小编给各位百度云的这一款软件使用者们带来的详细的百度云Windows版更改下载文件路径的方法解析分享的全部内容了,各位看到这里的软件使用者们小编相信大家现在那是非常的清楚了更改路径的方法了吧,那么大家就快去自己尝试下吧.

多线程断点下载文件

package cn.itcast.download; import java.io.File; import java.io.InputStream; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.URL; public class MulThreadDownloader {     public static void main(String[] args) throws

iOS开发网络篇—实现大文件的多线程断点下载_IOS

说明:本文介绍多线程断点下载.项目中使用了苹果自带的类,实现了同时开启多条线程下载一个较大的文件.因为实现过程较为复杂,所以下面贴出完整的代码. 实现思路:下载开始,创建一个和要下载的文件大小相同的文件(如果要下载的文件为100M,那么就在沙盒中创建一个100M的文件,然后计算每一段的下载量,开启多条线程下载各段的数据,分别写入对应的文件部分). 项目中用到的主要类如下: 完成的实现代码如下: 主控制器中的代码: #import "YYViewController.h" #import

我的Android进阶之旅------&amp;gt;Android基于HTTP协议的多线程断点下载器的实现

一.首先写这篇文章之前,要了解实现该Android多线程断点下载器的几个知识点  1.多线程下载的原理,如下图所示 注意:由于Android移动设备和PC机的处理器还是不能相比,所以开辟的子线程建议不要多于5条.当然现在某些高端机子的处理器能力比较强了,就可以多开辟几条子线程. 2.为了实现断点下载,采用数据库方式记录下载的进度,这样当你将该应用退出后,下次点击下载的时候,程序会去查看该下载链接是否存在下载记录,如果存在下载记录就会判断下载的进度,如何从上次下载的进度继续开始下载. 3.特别注意