Skip to content

Latest commit

 

History

History
217 lines (180 loc) · 7.4 KB

多线程断点下载.md

File metadata and controls

217 lines (180 loc) · 7.4 KB

多线程断点下载

  1. 多线程下载

    public class MultiThreadDownloader {
    
    	private URL url;        // 目标地址
    	private File file;        // 本地文件
    	private long threadLen;    // 每个线程下载多少
    
    	private static final int THREAD_AMOUNT = 3;                // 线程数
    	private static final String DIR_PATH = "F:/Download";    // 下载目录
    
    	public MultiThreadDownloader(String address) throws IOException {    
    		// 记住下载地址
    		url = new URL(address);                                                            
    		// 截取地址中的文件名, 创建本地文件
    		file = new File(DIR_PATH, address.substring(address.lastIndexOf("/") + 1));        
    	}
    
    	public void download() throws IOException {
    		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    		conn.setConnectTimeout(3000);
    
    		// 获取文件总长度
    		long totalLen = conn.getContentLength();                    
    		// 计算每个线程要下载的长度
    		// 总长度 如果能整除 线程数, 每条线程下载的长度就是 总长度 / 线程数
    		// 总长度 如果不能整除 线程数, 那么每条线程下载长度就是 总长度 / 线程数 + 1
    		threadLen = (totalLen + THREAD_AMOUNT - 1) / THREAD_AMOUNT;    
    
    		// 在本地创建一个和服务端大小相同的文件
    		RandomAccessFile raf = new RandomAccessFile(file, "rw");    
    		// 设置文件的大小, 写入了若干个0
    		raf.setLength(totalLen);                                    
    		raf.close();
    
    		// 按照线程数循环
    		for (int i = 0; i < THREAD_AMOUNT; i++) {
    			// 开启线程, 每个线程将会下载一部分数据到本地文件中 
    			new DownloadThread(i).start();       
    		}     
    	}
    
    	private class DownloadThread extends Thread {
    		// 用来标记当前线程是下载任务中的第几个线程
    		private int id;     
    
    		public DownloadThread(int id) {
    			this.id = id;
    		}
    
    		public void run() {
    			// 从临时文件读取当前线程已完成的进度
    
    			long start = id * threadLen;     
    			long end = id * threadLen + threadLen - 1;
    			System.out.println("线程" + id + ": " + start + "-" + end);
    
    			try {
    				HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    				conn.setConnectTimeout(3000);
    				// 设置当前线程下载的范围(start和end都包含)
    				conn.setRequestProperty("Range", "bytes=" + start + "-" + end);    
    
    				InputStream in = conn.getInputStream();
    				// 随机读写文件, 用来向本地文件写出
    				RandomAccessFile raf = new RandomAccessFile(file, "rw");        
    				// 设置保存数据的位置
    				raf.seek(start);                                                
    
    				// 每次拷贝100KB
    				byte[] buffer = new byte[1024 * 100];    
    				int len;
    				while ((len = in.read(buffer)) != -1) {
    					// 从服务端读取数据, 写到本地文件
    					raf.write(buffer, 0, len);            
    				}
    				raf.close();
    
    				System.out.println("线程" + id + "下载完毕");
    			} catch (IOException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    
    	public static void main(String[] args) throws IOException {
    		new MultiThreadDownloader("http://192.168.1.240:8080/14.Web/android-sdk_r17-windows.zip").download();
    	}
    }
  2. 断点下载

    public class BreakpointDownloader {
    	// 下载目录
    	private static final String DIR_PATH = "F:/Download";    
    	// 总线程数
    	private static final int THREAD_AMOUNT = 3;                
    
    	// 目标下载地址
    	private URL url;            
    	// 本地文件
    	private File dataFile;        
    	// 用来存储每个线程下载的进度的临时文件
    	private File tempFile;        
    	// 每个线程要下载的长度
    	private long threadLen;      
    	// 总共完成了多少  
    	private long totalFinish;    
    	// 服务端文件总长度
    	private long totalLen;        
    	// 用来记录开始下载时的时间
    	private long begin;            
    
    	public BreakpointDownloader(String address) throws IOException {    
    		url = new URL(address);                                                            
    		dataFile = new File(DIR_PATH, address.substring(address.lastIndexOf("/") + 1));    
    		tempFile = new File(dataFile.getAbsolutePath() + ".temp");                        
    	}
    
    	public void download() throws IOException {
    		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    		conn.setConnectTimeout(3000);
    
    		totalLen = conn.getContentLength();                                    
    		threadLen = (totalLen + THREAD_AMOUNT - 1) / THREAD_AMOUNT;            
    
    		if (!dataFile.exists()) {                                            
    			RandomAccessFile raf = new RandomAccessFile(dataFile, "rws");    
    			raf.setLength(totalLen);                                        
    			raf.close();
    		}
    
    		if (!tempFile.exists()) {                                            
    			RandomAccessFile raf = new RandomAccessFile(tempFile, "rws");    
    			for (int i = 0; i < THREAD_AMOUNT; i++)                           
    				raf.writeLong(0);                                            
    			raf.close();
    		}
    
    		for (int i = 0; i < THREAD_AMOUNT; i++) { 
    			new DownloadThread(i).start();       
    		} 
    
    		// 记录开始时间
    		begin = System.currentTimeMillis();        
    	}
    
    	private class DownloadThread extends Thread {
    		// 用来标记当前线程是下载任务中的第几个线程
    		private int id;     
    
    		public DownloadThread(int id) {
    			this.id = id;
    		}
    
    		public void run() {
    			try {
    				RandomAccessFile tempRaf = new RandomAccessFile(tempFile, "rws");        
    				tempRaf.seek(id * 8);                        
    				long threadFinish = tempRaf.readLong();        
    				synchronized(BreakpointDownloader.this) {    
    					totalFinish += threadFinish;            
    				}
    
    				long start = id * threadLen + threadFinish;      
    				long end = id * threadLen + threadLen - 1;       
    				System.out.println("线程" + id + ": " + start + "-" + end);
    
    				HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    				conn.setConnectTimeout(3000);
    				conn.setRequestProperty("Range", "bytes=" + start + "-" + end);        
    
    				InputStream in = conn.getInputStream();                              
    				RandomAccessFile dataRaf = new RandomAccessFile(dataFile, "rws");    
    				dataRaf.seek(start);                                                
    
    				byte[] buffer = new byte[1024 * 100];            
    				int len;
    				while ((len = in.read(buffer)) != -1) {
    					dataRaf.write(buffer, 0, len);               
    					threadFinish += len;                        
    					tempRaf.seek(id * 8);                        
    					tempRaf.writeLong(threadFinish);           
    					synchronized(BreakpointDownloader.this) {   
    						totalFinish += len;                        
    					}
    				}
    				dataRaf.close();
    				tempRaf.close();
    
    				System.out.println("线程" + id + "下载完毕");
    				// 如果已完成长度等于服务端文件长度(代表下载完成)
    				if (totalFinish == totalLen) {                    
    					System.out.println("下载完成, 耗时: " + (System.currentTimeMillis() - begin));
    					// 删除临时文件
    					tempFile.delete();                            
    				}
    			} catch (IOException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    
    	public static void main(String[] args) throws IOException {
    		new BreakpointDownloader("http://192.168.1.240:8080/14.Web/android-sdk_r17-windows.zip").download();
    	}
    }