这期内容当中小编将会给大家带来有关怎么在Java中利用多线程实现分片下载文件,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。
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 (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(); } } }
上述就是小编为大家分享的怎么在Java中利用多线程实现分片下载文件了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注亿速云行业资讯频道。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。