zoukankan      html  css  js  c++  java
  • 多线程下载,以及断点的实现

    import java.io.File;
    import java.io.FileInputStream;
    import java.io.InputStream;
    import java.io.RandomAccessFile;
    import java.net.HttpURLConnection;
    import java.net.URL;
    
    /**
     * 多线程下载,以及断点下载的实现<br>
     * 当中有个不好的地方,<br>
     * 就是进度文件的保存的时候假设採用RandomAccessFile的方式进行保存的时候<br>
     * 尽管会将文件的进度时时的保存在进度文件里,<br>
     * 可是,经过实际的測试这样会大大的减少文件的下载的速度,<br>
     * 假设採用File和FileOutputStream的话尽管能够加快下载的速度<br>
     * 可是进度文件的时时写入会出现故障.<br>
     * 
     * <br>
     * 眼下我还没有找到非常好的解决方案,假设大家有的话欢迎给我留言.
     * 
     * @author MartinDong
     * 
     */
    public class Demo {
    	// 定义线程个数
    	public static int threadCount = 3;
    	// 定义当前存货的线程个数
    	public static int runningThread = 3;
    
    	public static void main(String[] args) throws Exception {
    		// 1,连接到server,获取一个文件,获取文件的大小跟server的文件一样的暂时文件
    		String path = "http://172.22.64.193:8080/test.exe";
    		URL url = new URL(path);
    		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    		// 设置超时
    		conn.setConnectTimeout(5000);
    		// 设置请求方式
    		conn.setRequestMethod("GET");
    		// 获取server的返回码
    		int code = conn.getResponseCode();
    		// 推断返回码
    		if (code == 200) {
    			// 获取返回的长度
    			int length = conn.getContentLength();
    			System.out.println("文件总长度:" + length);
    
    			// 在client创建出一个跟server大小一致的暂时文件
    			RandomAccessFile raf = new RandomAccessFile("test.exe", "rwd");
    			// 指定暂时文件的大小
    			raf.setLength(length);
    			// 释放资源
    			raf.close();
    
    			// 平均每个线程的文件大小
    			int blockSize = length / threadCount;
    
    			for (int threadId = 1; threadId <= threadCount; threadId++) {
    				// 线程開始的下载位置
    				int startIndex = (threadId - 1) * blockSize;
    				// 线程的结束位置
    				int endIndex = threadId * blockSize - 1;
    				// 推断是否是最后一个线程
    				if (threadId == threadCount) {
    					// 设置结束的位置为到文件的最后
    					endIndex = length;
    				}
    				System.out.println("线程:" + threadId + "下载:>>>>>>>>"
    						+ startIndex + ">>>>>>>>>>" + endIndex);
    
    				new DownlodeThread(path, threadId, startIndex, endIndex)
    						.start();
    			}
    		}
    	}
    
    	/**
    	 * 下载文件的子线程类,每个线程下载相应位置文件数据
    	 * 
    	 * @author MartinDong
    	 * 
    	 */
    	public static class DownlodeThread extends Thread {
    		private String path;
    		private int threadId;
    		private int startIndex;
    		private int endIndex;
    
    		/**
    		 * 
    		 * @param path
    		 *            文件的下载路径
    		 * @param threadId
    		 *            线程id
    		 * @param startIndex
    		 *            线程開始的位置
    		 * @param endIndex
    		 *            线程结束的位置
    		 */
    		public DownlodeThread(String path, int threadId, int startIndex,
    				int endIndex) {
    			this.path = path;
    			this.threadId = threadId;
    			this.startIndex = startIndex;
    			this.endIndex = endIndex;
    		}
    
    		@Override
    		public void run() {
    			try {
    				// 检查是否存在下载历史的文件
    				File tempFile = new File(threadId + ".txt");// =========================断点记录操作===============================
    				if (tempFile.exists() && tempFile.length() > 0) {
    					// 文件输入流
    					FileInputStream fis = new FileInputStream(
    							tempFile);
    					// 中间变量,缓存的作用
    					byte[] tempBuffer = new byte[1024];
    					// 获取进度文件的数据大小
    					int length = fis.read(tempBuffer);
    					// 获取进度文件的数据
    					String historyData = new String(tempBuffer, 0, length);
    					// 将进度数据装换为整型
    					int historyDataInt = Integer.parseInt(historyData);
    					// 改动真正的下载位置
    					startIndex = historyDataInt;
    					fis.close();
    				}// =========================断点记录操作===============================
    
    				// 将地址转换为URL
    				URL url = new URL(path);
    				// 获取http连接
    				HttpURLConnection conn = (HttpURLConnection) url
    						.openConnection();
    				// 设置连接的请求方式
    				conn.setRequestMethod("GET");
    				// 重要:请求server下载部分的文件,指定文件的位置
    				conn.setRequestProperty("Range", "bytes=" + startIndex + "-"
    						+ endIndex);
    
    				System.out
    						.println("线程:" + threadId + "真实開始的下载进度:" + startIndex);
    				// 设置超时时间
    				conn.setReadTimeout(5000);
    				// 得到server的状态码,200表示请求的所有资源得到响应=== ok,206请求的部分资源得到响应=== ok
    				int code = conn.getResponseCode();
    				System.out.println("code:" + code);
    
    				if (code == 206) {
    					// 返回的是指定位置的文件流
    					InputStream is = conn.getInputStream();
    					// 创建一个暂时的文件
    					RandomAccessFile raf = new RandomAccessFile("test.exe",
    							"rwd");
    					// 移动指针,到指定的文件位置,
    					raf.seek(startIndex);
    
    					// 创建中间缓冲字节数组
    					byte[] buffer = new byte[1024];
    					// 读取文件的大小
    					int length = 0;
    
    					// 定义已经下载的数据长度,用作断点下载的记录=========================断点记录操作===============================
    					int downlodeTotal = 0;
    					// 循环写入
    					while ((length = is.read(buffer)) != -1) {
    						// 定义一个记录线程的记录文件=========================断点记录操作===============================
    						RandomAccessFile historyFile = new RandomAccessFile(
    								threadId + ".txt", "rwd");
    						// 向文件里写入数据
    						raf.write(buffer, 0, length);
    						// 记录已经下载的文件长度
    						downlodeTotal += length;
    						// 将已经下载的文件长度和開始的读取位置相加,得到已经读取的文件位置
    						historyFile.write((downlodeTotal + startIndex + "")
    								.getBytes());
    						historyFile.close();// =========================断点记录操作===============================
    						System.out.println("线程:" + threadId + "已下载:"
    								+ downlodeTotal);
    					}
    					is.close();
    					raf.close();
    					System.out.println("线程:" + threadId + "完成下载............");
    
    				} else {
    					System.out.println("线程:" + threadId
    							+ "下载失败请又一次下载............");
    				}
    			} catch (Exception e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			} finally {
    				// 进行线程数量的变化操作
    				runningThread--;
    				// 假设当前存活的线程为0,运行进度文件统一销毁的操作
    				if (runningThread == 0) {
    					// 假设完成下载,清除进度文件
    					for (int threadIndex = 1; threadIndex <= threadCount; threadIndex++) {
    						// 这里创建的是与线程文件相应的文件,文件名称能够自己定义,方便起见是採用的是线程的ID表示
    						File temp = new File(threadIndex + ".txt");
    						// 运行文件删除的操作
    						temp.delete();
    					}
    					System.out.println("文件完成下载,删除进度文件.............");
    				}
    			}
    		}
    	}
    
    }
    


  • 相关阅读:
    zabbix自动发现 url 以及对http返回状态码监控实现 告警
    iOS 开发者账号到期续费流程
    iOS 开发 -----公司测试打包上传流程
    go语言 二叉树
    GO 语言队列实现
    GO 语言常用排序
    go 语言实现栈原理
    循环链表实现原理及运用约瑟夫环实例
    双向链表实现原理
    单向链表实现原理
  • 原文地址:https://www.cnblogs.com/clnchanpin/p/6752176.html
Copyright © 2011-2022 走看看