集训day_08_2(多线程下载Java实现)

一、多线程加速下载下载

1.不是说线程开的越多下载就越快  例:手机迅雷(建议3-4个线程)

2.还受服务器带宽的影响

3.相当于更多的cpu资源给了你

二、多线程下载步骤分析

1.获取文件大小

2.在客户端创建一个大小和服务器一模一样的文件,提前申请好空间

3.每个线程下载的开始位置和结束位置

4.开多个线程去下载文件

4.知道每个线程什么时候下载完毕了

扫描二维码关注公众号,回复: 2415927 查看本文章

1.获取文件的大小

public static void main(String[] args) {
	
	//【一★★★★★★★】获取服务器文件大小
	new Thread() {
		@Override
		public void run() {
			try {
				//【2】定义下载路径
				String path = "http://192.168.3.138:8080/ttt.exe";
				//【2.2】创建URL对象指定我们要访问的网址(路径)
				URL url = new URL(path);
				//【2.3】拿到HttpURLConnection对象 用于发送或接受数据
				HttpURLConnection conn = (HttpURLConnection) url.openConnection();
				//【2.4】设置发送get请求
				conn.setRequestMethod("GET");
				//【2.5】设置请求时间
				conn.setConnectTimeout(5000);
				//【2.6】获取服务器返回的状态码
				int code = conn.getResponseCode();
				//【2.7】如果code == 200说明请求成功
				if (code == 200) {
					//★★★★★获取服务器文件大小
					int length = conn.getContentLength();
					System.out.println("length:"+length);
					
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}.start();
}

2.每个线程下载的计算公式

 

3.实现代码

package down;

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

public class MutioDown {

	// 【1】定义下载路径
	static String path = "http://192.168.3.138:8080/ttt.exe";
	private static final int threadCount = 3;// 假设开三个线程
	private static int runningThread;

	public static void main(String[] args) {

		try {
			// 【2】创建URL对象指定我们要访问的网址(路径)
			URL url = new URL(path);
			// 【3】拿到HttpURLConnection对象 用于发送或接受数据
			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
			// 【4】设置发送get请求
			conn.setRequestMethod("GET");
			// 【5】设置请求时间
			conn.setConnectTimeout(5000);
			// 【6】获取服务器返回的状态码
			int code = conn.getResponseCode();
			// 【6.1】如果code == 200说明请求成功
			if (code == 200) {
				// 【7】获取服务器文件大小
				int length = conn.getContentLength();
				runningThread = threadCount;
				System.out.println("length:" + length);

				// 【二★★★★★★★】创建一个大小和服务器一模一样的文件,目的提前把空间申请出来
				RandomAccessFile rafAccessFile = new RandomAccessFile(
						getFilename(path), "rw");
				rafAccessFile.setLength(length);

				// 【7】计算出每个线程下载的大小
				int blockSize = length / threadCount;

				// 【三★★★★★★★】计算每个线程下载的开始位置和结束位置
				for (int i = 0; i < threadCount; i++) {
					int starIndex = i * blockSize; // 每个线程下载的开始位置
					int endIndex = (i + 1) * blockSize - 1;// 每个线程下载的结束位置
					// 特殊情况 就是最后一个线程
					if (i == threadCount - 1) {
						endIndex = length - 1;
					}

					// 【四★★★★★★★】开启线程去服务器下载文件
					new DownLoadThread(starIndex, endIndex, i).start();

				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private static class DownLoadThread extends Thread {

		// 通过构造方法把每个线程下载的开始位置和结束位置传递进来
		private int starIndex;
		private int endIndex;
		private int threadId;

		public DownLoadThread(int starIndex, int endIndex, int threadId) {
			this.starIndex = starIndex;
			this.endIndex = endIndex;
			this.threadId = threadId;
		}

		@Override
		public void run() {
			// 【四★★★★★★】实现去服务器下载的逻辑
			try {
				URL url = new URL(path);
				HttpURLConnection conn = (HttpURLConnection) url
						.openConnection();
				conn.setRequestMethod("GET");
				conn.setConnectTimeout(5000);

				// 如果中间断过,继续在上次的位置继续下载,从文件中读取上次下载的位置
				File file = new File(getFilename(path) + threadId + ".txt");
				if (file.exists() && file.length() > 0) {
					FileInputStream fis = new FileInputStream(file);
					BufferedReader bufr = new BufferedReader(
							new InputStreamReader(fis));
					String lastPositionn = bufr.readLine(); // 读取出来的内容就是上次下载的位置
					int lastPosition = Integer.parseInt(lastPositionn);

					// 要改变一下startIndex的位置
					starIndex = lastPosition;
					fis.close();
				}

				// ★★★★★设置一个请求头Range(作用告诉服务器每个线程下载的开始位置和结束位置)
				conn.setRequestProperty("Range", "bytes=" + starIndex + "-"
						+ endIndex);

				int code = conn.getResponseCode();
				if (code == 206) { // 200表示获取服务器资源全部成功,206表示请求部分资源成功

					// 创建随机读写文件对象
					RandomAccessFile raf = new RandomAccessFile(
							getFilename(path), "rw");
					// 每个线程要从自己的位置开始写
					raf.seek(starIndex);

					InputStream in = conn.getInputStream(); // 存的是ttt.exe

					// 把数据写到文件中
					int len = -1;
					byte[] buffer = new byte[1024 * 1024];

					int total = 0; // 代表当前线程下载的大小

					while ((len = in.read(buffer)) != -1) {
						raf.write(buffer, 0, len);

						// 实现断点续传 就是把当前线程下载的位置存起来 下次再下载的时候按上次下载的位置继续下载就可以了
						total += len;
						int currentThreadPosition = starIndex = total;
						RandomAccessFile raff = new RandomAccessFile(
								getFilename(path) + threadId + ".txt", "rwd");
						raff.write(String.valueOf(currentThreadPosition)
								.getBytes());
						raff.close();
					}
					raf.close();// 释放资源

					System.out.println("线程:" + threadId + "--下载完了");
					synchronized (DownLoadThread.class) {
						runningThread--;
						if (runningThread == 0) { // 说明所有线程都执行完毕了
							for (int i = 0; i < threadCount; i++) {
								File deleFile = new File(getFilename(path) + i
										+ ".txt");
								deleFile.delete();
							}
						}
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	public static String getFilename(String path) { // 获取文件的名字

		int start = path.lastIndexOf("/") + 1;
		return path.substring(start);
	}

}

 

猜你喜欢

转载自blog.csdn.net/Depths_p/article/details/81218830