原理
多线程下载的原理就是将要下载的文件分成若干份,其中每份都使用一个单独的线程进行下载,这样对于文件的下载速度自然就提高了许多。
既然要分成若干部分分工下载,自然要知道各个线程自己要下载的起始位置,与要下载的大小。所以我们要解决线程的分配与各个线程定位到下载的位置。
封装
对于多线程下载我们可以将其封装到一个工具类中DownUtil
,向其中传入下载的链接、文件存储路径、需要下载的线程数
1 2 3 4 5 6
| public DownUtil(String path, String targetFile, int threadNum) { this.path = path; this.targetFile = targetFile; this.threadNum = threadNum; downThreads = new DownThread[threadNum]; }
|
其中DownThread
实现的是各个线程的下载
分配线程
这里通过HttpURLConnection
进行网络请求下载,通过getContentLength()
方法获取下载文件的总大小,再对其平均分配各个线程需要下载的大小。这样就确定了下载的大小,下面就是定位到各个线程的开始位置进行下载,这里可以使用RandomAccessFile
来追踪定位到要下载的位置,它的seek()
方法可以进行定位。下面是详细代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| public void download() throws Exception { URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(5000); conn.setRequestProperty("Accept-Language", "zh-CN"); conn.setRequestProperty("Charset", "UTF-8"); conn.setRequestProperty("Connection", "Keep-Alive"); fileSize = conn.getContentLength(); conn.disconnect(); //需要下载的大小 int currentPartSize = fileSize / threadNum + 1; RandomAccessFile file = new RandomAccessFile(targetFile, "rw"); //设置本地文件大小 file.setLength(fileSize); file.close(); for (int i = 0; i < threadNum; i++) { //下载开始位置 int startPos = i * currentPartSize; RandomAccessFile currentPart = new RandomAccessFile(targetFile, "rw"); //定位到下载位置 currentPart.seek(startPos); //下载线程 downThreads[i] = new DownThread(startPos, currentPartSize, currentPart); downThreads[i].start(); } }
|
线程下载
下面就是各个线程的下载DownThread
,上面已经得到了各个线程要下载的初始位置,所以可以通过获取网络请求的输入流InputStream
,通过skip()
方法跳跃到指定位置进行读取数据,再写入到RandomAccessFile
文件中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| public DownThread(int startPos, int currentPartSize, RandomAccessFile currentPart) { this.startPos = startPos; this.currentPartSize = currentPartSize; this.currentPart = currentPart; } @Override public void run() { try { URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(5000); conn.setRequestProperty("Accept-Language", "zh-CN"); conn.setRequestProperty("Charset", "UTF-8"); InputStream in = conn.getInputStream(); skipFully(in, startPos); byte[] buffer = new byte[1024]; int hasRead = 0; while ((hasRead = in.read(buffer)) > 0 && length < currentPartSize) { currentPart.write(buffer, 0, hasRead); length += hasRead; } currentPart.close(); in.close(); } catch (Exception e) { e.printStackTrace(); } } }
|
这样就完成了一个简单的多线程的下载,最后调用封装类DownUtil
就可以进行多线程下载。
总结
多线程的关键就是分配好需要下载的进程,定位进程下载的准确位置,获取输入流读取数据,同时写入到文件的相应位置。可以借助RandomAccessFile
来进行定位。
当然也并非开的线程数越多下载的速度也就越快,因为线程越多对于程序处理这些线程也是一种负担,过多的话反而会降低下载的速度,所以要合理运用。
转载请指明出处 idisfkj博客:https://idisfkj.github.io