Java URLConnection类 实现多线程下载文件

多线程下载文件是很实用的加速技巧,今天介绍多线程下载文件

前文回顾
Java 两种简单的方式实现多线程
Java ExecutorService管理线程池的简单用法
JAVA URLConnection 实现下载小文件

单一线程下载文件的步骤

  1. 建立URLConnection对象并获取输入流
  2. 建立文件输出流
  3. 将输入流的内从写入到输出流

多线程下载

多线程下载其实就是将文件分为多个部分,每个线程负责一个部分
在这里插入图片描述
在一些场景,服务器可能限制了每个连接的最大传输速度,那我们可以通过建立多个连接,并行下载,达到加速的效果

如何向服务器获取部分文件

因为多线程下载,每个线程只需要负责一个部分,那么如何向服务器请求一小部分文件而不是全部呢?

这里要用到http包头的Range字段,比如从4396字节处开始,到7777字节处结束,对应的Range包头字段为:

Range: bytes:4396-7777

通过调用URLConnection对象的setRequestProperty方法可以设置请求头:

long st = 4396;	// 开始处
long ed = 7777;	// 结束处
URLConnection con = new URL("http://xxx.com").openConnection();
con.setRequestProperty("Range", "bytes="+String.valueOf(st)+"-"+String.valueOf(ed))

整体思路

  1. 创建一个URLConnection对象并且获取文件长度
  2. 创建多个URLConnection对象,设置请求头为请求文件的不同部分
  3. 创建对应的文件输出流,使用RandomAccessFile以保证文件写入的有序性
  4. 开启一个下载线程(自定义的类),这个线程接收URLConnection对象和RandomAccessFile对象,以完成文件的写入

下载线程的编写

import java.net.*;
import java.util.*;
import java.util.concurrent.*;
import java.io.*;

public class DownloadThread extends Thread {
	
	URLConnection con;
	RandomAccessFile rf;
	int tid = 0;
	
	public static volatile long allcur = 0;
	public static boolean isReady = false;
	
	public DownloadThread() {
		
	}
	
	public DownloadThread(URLConnection con, RandomAccessFile rf) {
		this.con = con;
		this.rf = rf;
	}
	
	public void run() {
		try {	
			// 获取输入输出流
			InputStream is = con.getInputStream();
			
			// 输入流内容写入输出流
			byte[] buf = new byte[1024*1024];
			int len = -1;
			while((len=is.read(buf)) != -1) {
				rf.write(buf, 0, len);
				// 同步更新所有线程的进度 allcur是线程的共享变量 表示当前下载进度
				synchronized(new Object()) {
					allcur += (long)len;
				}
			}
			rf.close();
			
		} catch(Exception e) {
			e.printStackTrace();
		}
	}
}

main函数代码

String url = "http://www.szulrl.cn/browserTest/PDFFILE.zip";	// 目标url地址
int tnum = 3;	// 线程个数
final long totalLength = new URL(url).openConnection().getContentLength();
long eachLength = totalLength/tnum;	// 每一部分下载多少字节
String loc = "C:/"	// 保存路径
String fname = "downloadFile";	// 文件名
String ext = ".7z";	// 后缀名	

for(int i=0; i<tnum; i++) {		
	long st = eachLength * i;
	long ed = eachLength * (i+1);
	if(i == tnum-1) ed=Math.max(ed, totalLength);
	
	URLConnection con = new URL(url).openConnection();
	con.setRequestProperty("Range", "bytes="+String.valueOf(st)+"-"+String.valueOf(ed));
	con.connect();

	RandomAccessFile rf = new RandomAccessFile(loc+fname+ext, "rw");
	rf.seek(st);	// 文件流跳转到对应位置
	
	pool.submit(new DownloadThread(con, rf));
}
相关推荐
©️2020 CSDN 皮肤主题: 博客之星2020 设计师:CY__ 返回首页