Java多线程文件分片下载实现的示例代码

网友投稿 548 2022-12-12


Java多线程文件分片下载实现的示例代码

多线程下载介绍

多线程下载技术是很常见的一种下载方案,这种方式充分利用了多线程的优势,在同一PSPgVgqYNK时间段内通过多个线程发起下载请求,将需要下载的数据分割成多个部分,每一个线程只负责下载其中一个部分,然后将下载后的数据组装成完整的数据文件,这样便大大加快了下载效率。常见的下载器,迅雷,QQ旋风等都采用了这种技术。

分片下载

所谓分片下载就是要利用多线程的优势,将要下载的文件一块一块的分配到各个线程中去下载,这样就极大的提高了下载速度。

技术难点

并不能说是什么难点,只能说没接触过不知道罢了。

1、如何请求才能拿到数据的特定部分,而非全部?

可以在HTTP请求头中加入Range来标识数据的请求范围/区间,从HTTP/1.1开始可用。

基本用法:

Range: bytes=10-:取第10个字节及后所有数据。

Range: bytes=40-100:取第40个字节到第100个字节之间的数据。

这样我们就能拿到特定部分的数据了,断点续传也可以用这个来实现。

PS:0为开始点。

2、分片后某线程下载时如何写出?

思路1:等所有下载完成后进行统一汇总整理然后再一次性写出。

这简直是最笨的思路了,如果文件过大全部拉到内存中,岂不凉凉。

思路2:下载采用多线程,写出时采取数据前后顺序排队写出。

也就是说多线程下载,单线程输出,某种程度解决了内存占用问题,不过效率基本不理想。

思路3:要说还是API香,老大哥java给我们提供了一个类叫做RandomAccessFile。

这个类可以进行随机文件读写,其中有一个seek函数,可以将指针指向任意位置,然后进行读写。什么意思呢,举个栗子:假如我们开了30个线程,首先第一个下载完成的是线程X,它下载的数据范围是4000-9000,那么这时我们调用seek函数将指针拨动到4000,然后调用它的write函数将byte写出,这时4000之前都是NULL,4000之后就是我们插入的数据。这样就可以实现多线程下载和本地写入了。

具体实现

一个分片下载类,我们需要创建多个对象来进行下载。

public class UnitDownloader implements Runnable {

private int from;

private int to;

private File target;

private String uri;

private int id;

public UnitDownloader(int from, int to, File target, String uri, int id) {

this.from = from;

this.to = to;

this.target = target;

this.uri = uri;

this.id = id;

}

public int getFrom() {

return from;

}

public int getTo() {

return to;

}

@Override

public void run() {

//download and save data

try {

HttpURLConnection connection = (HttpURLConnection) new URL(uri).openConnection();

connection.setRequestProperty("Range", "bytes=" + from + "-" + to);

connection.connect();

int totalSize = connection.getContentLength();

InputStream inputStream = connection.getInputStream();

RandomAccessFile randomAccessFile = new RandomAccessFile(target, "rw");

randomAccessFile.seek(from);

byte[] buffer = new byte[1024 * 1024];

int readCount = inputStream.read(buffer, 0, buffer.length);

while (http://readCount > 0) {

totalSize -= readCount;

System.out.println("分片:" + this.id + "的剩余:" + totalSize);

randomAccessFile.write(buffer, 0, readCount);

readCount = inputStream.read(buffer, 0, buffer.length);

}

inputStream.close();

randomAccessFile.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

分片下载管理器,主要就是拿到内容的总大小,将其分配给每一个UnitDownloader。这里的threadCount函数可以再考虑优化一下。

public class MultipleThreadDownloadManager implements Runnable {

private String uri;

private File target;

public MultipleThreadDownloadManager(String uri, File target) {

this.target = target;

this.uri = uri;

if (target.exists() == false) {

try {

target.createNewFile();

} catch (IOException e) {

e.printStackTrace();

}

}

}

/**

* 开始下载

*/

public void start() {

new Thread(this).start();

}

/**

* 根据文件总大小计算线程数量

*

* @param totalSize

* @return

*/

public int threadCount(int totalSize) {

if (totalSize < 30 * 2014 * 1024) {

return 1;

}

return 30;

}

@Override

public void run() {

//获取文件总大小

int totalSize = 0;

try {

HttpURLConnection connection = (HttpURLConnection) new URL(uri).openConnection();

connection.connect();

int contentLength = connection.getContentLength();

totalSize = contentLength;

} catch (IOException e) {

e.printStackTrace();

}

//将文件分片并分开下载

int threadCount = threadCount(totalSize);

int perThreadSize = totalSize / threadCount;//每一个线程分到的任务下载量

int id = 0;

int from = 0, to = 0;

while (totalSize > 0) {

id++;

//计算分片

if (totalSize < perThreadSize) {

from = 0;

to = totalSize;

} else {

from = totalSize;

to = from + perThreadSize;

}

//开始下载

UnitDownloader downloader = new UnitDownloader(from, to, target, uri, id);

new Thread(downloader).start();

}

}

}

参考文献

1、https://emacsist.github.io/2015/12/29/http-%E5%8D%8F%E8%AE%AE%E4%B8%AD%E7%9A%84range%E8%AF%B7%E6%B1%82%E5%A4%B4%E4%BE%8B%E5%AD%90/

2、https://blog.csdn.net/lyt_7cs1dn9/article/details/75105266


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Spring Boot Rest控制器单元测试过程解析
下一篇:基于Spring Boot保护Web应用程序
相关文章

 发表评论

暂时没有评论,来抢沙发吧~