当前位置 : 主页 > 编程语言 > c++ >

客户端多线程断点下载(优化多线程下载)

来源:互联网 收集:自由互联 发布时间:2021-06-30
客户端多线程断点下载(优化多线程下载) package com.thread;import java.io.File;import java.io.FileInputStream;import java.io.InputStream;import java.io.RandomAccessFile;import java.net.HttpURLConnection;import java.net.URL;/**
客户端多线程断点下载(优化多线程下载)
package com.thread;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;

/**
 * 多线程下载的测试类
 * 
 * @author Administrator
 *
 */
public class MutiThreadDownloadr {
	public static int threadCount = 3; // 线程数量
	public static int runningThread = 3; // 运行着的线程数,下面代码会判断逐一减少,直到0

	public static void main(String[] args) throws Exception {

		// 1.连接服务器,获取一个文件的长度。在本地创建一个大小与服务器文件一样大的临时文件
		String path = "http://localhost:8090/jdk.exe";
		URL url = new URL(path);

		// 获取连接
		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
		// 设置参数
		conn.setConnectTimeout(5000);
		conn.setRequestMethod("GET");
		int code = conn.getResponseCode();
		if (code == 200) {
			// 服务器返回的数据的长度,实际就是文件的长度
			int length = conn.getContentLength();
			System.out.println("文件总长度:" + length);

			// 在客户端本地创建一个与服务器文件一样大的临时文件
			// 需要用到RandomAccessFile的API,可以用指针对应到某块文件的位置上
			// 第二个参数:r代表以只读方式打开;
			// rw代表读取和写入,数据的更新会先放到缓存中;
			// rws代表读取和写入,并且元数据的更新立刻写入底层设备
			// rwd代表读取和写入,并且内容的更新立刻写入底层设备
			RandomAccessFile raf = new RandomAccessFile("setup.exe", "rwd");
			// 指定创建临时文件的长度,应该与服务器上的长度相同
			raf.setLength(length);
			raf.close(); // 关闭流

			// 2.假设3个线程去下载资源
			// 平均每一个线程下载的文件的大小
			int blocksize = length / threadCount; // 每个线程下载的文件大小

			for (int threadid = 1; threadid <= threadCount; threadid++) {
				// 每个线程下载的开始位置 ==> (线程id-1)*每一块大小
				int startIndex = (threadid - 1) * blocksize;
				// 每个线程的结束位置 ==> (线程id*每一块大小)-1
				int endIndex = threadid * blocksize - 1;
				// 如果线程数量没有被整除,则最后一个线程下载的长度要长一些
				if (threadid == threadCount) {
					endIndex = length;
				}
				System.out.println("线程:" + threadid + "下载:--" + startIndex + "-->" + endIndex);

				// 4.在主线程中,开启多线程下载
				new DownloadThread(threadid, startIndex, endIndex, path).start();

			}

		} else {

			System.out.println("服务器错误");
		}
	}

	/**
	 * 下载文件的子线程,每一个线程下载对应位置的文件 在子线程中,需要关注4个参数: 1.线程id号 2.起始位置 3.结束位置 4.路径
	 *
	 */
	public static class DownloadThread extends Thread {
		private int threadId;
		private int startIndex;
		private int endIndex;
		private String path;

		// 使用构造方法,初始化以上的参数
		public DownloadThread(int threadId, int startIndex, int endIndex, String path) {
			this.threadId = threadId;
			this.startIndex = startIndex;
			this.endIndex = endIndex;
			this.path = path;
		}

		@Override
		public void run() {
			try {

				// =========断点续传:检查是否存在记录下载长度的文件,如果存在读取这个文件的数据
				File tempFile = new File(threadId + ".txt");
				// 判断只有是文件存在的情况下才断点续传
				if (tempFile.exists() && tempFile.length() > 0) {

					FileInputStream fis = new FileInputStream(threadId + ".txt");
					byte[] temp = new byte[1024];
					int leng = fis.read(temp);
					String downloadok_length = new String(temp, 0, leng); // 记录已经下载的长度
					int downloadokInt = Integer.parseInt(downloadok_length); // 转化为int类型
					startIndex = downloadokInt; // 修改下载的真实的开始位置
					fis.close();
				}

				URL url = new URL(path);
				HttpURLConnection conn = (HttpURLConnection) url.openConnection();
				conn.setRequestMethod("GET");
				conn.setConnectTimeout(5000);

				// 重要:::设置Range属性的范围:请求服务器下载部分的文件指明文件的位置
				// 格式为:"Range"对应"bytes=开始位置-结束位置"
				conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);

				System.out.println("线程真实下载:" + threadId + "下载:--" + startIndex + "-->" + endIndex);

				int code = conn.getResponseCode();// 全部资源为200,部分资源为206

				if (code == 206) {

					// 由于上面设置了Range属性请求的位置
					// 那么getInputStream()返回的是当前位置对应的文件的输入流
					InputStream is = conn.getInputStream();

					RandomAccessFile raf = new RandomAccessFile("setup.exe", "rwd");
					// 重要:seek()作用是在写文件的时候,从哪个位置开始写
					raf.seek(startIndex); // 每个线程使用startIndex来定位文件

					// 循环读InputStream,写入RandomAccessFile中即可
					int len = 0;
					byte[] buffer = new byte[1024];

					// =========断点续传:已经下载的长度
					int total = 0;
					// =========断点续传:记录当前线程下载的数据长度
					while ((len = is.read(buffer)) != -1) {
						RandomAccessFile file = new RandomAccessFile(threadId + ".txt", "rwd"); // 线程的临时文件
						raf.write(buffer, 0, len);
						total += len; // =========断点续传:累计
						//System.out.println("线程:" + threadId + "total:" + total);
						file.write((total + startIndex + "").getBytes()); // 优化:记录下载的位置
						file.close();
					}
					is.close();
					raf.close();
					System.out.println("线程:" + threadId + "下载完毕了...");

				} else {
					System.out.println("线程:" + threadId + "下载失败。。。");
				}

			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				// 只要一个线程结束了,就将runningThread减一
				runningThread--;
				if (runningThread == 0) { // 判断所有线程已经执行完毕了
					for (int i = 1; i <= 3; i++) {
						File file = new File(i + ".txt");
						file.delete();
					}
					System.out.println("文件下载完毕,删除所有的下载记录");
				}
			}
		}
	}
}
网友评论