多线程下载
思路:
将文件分成多个部分,多个线程进行读取和写入,如3个线程下载1个大小为902byte的文件,第一个线程下载0-299字节,第二个线程下载300-599字节,第三个线程下载剩下的即600-901字节
需要用的知识点:
HTTP Range
HTTP1.1中,Range请求头可以指定下载的范围,从0开始计算
Range 请求头格式
Range: bytes=start-end
例如:
Range: bytes=0-39:第0个字节到第39个字节之间的数据
Range: bytes=40-100:第40个字节到最后一个字节的数据.
注意,这个表示[start,end],即是包含请求头的start及end字节的,所以,下一个请求,应该是上一个请求的[end+1, nextEnd]
响应头
Content-Range
Content-Range: bytes 0-10/3103
这个表示,服务器响应了前(0-10)个字节的数据,该资源一共有(3103)个字节大小。
RandomAccessFile
RandomAccessFile支持随机访问文件,通过seek()方法移动到指定位置进行读写
package io; import java.io.File; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; import java.util.concurrent.CountDownLatch; public class MultiThreadDownloadFile { public static void main(String []args) throws IOException, InterruptedException { URL url = new URL( "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1489511816166&di=c47b4abe7e4dad502b0672b8065c0fb6&imgtype=0&src=http%3A%2F%2Fimg.taopic." + "com%2Fuploads%2Fallimg%2F111130%2F573-1111301A01938.jpg"); HttpURLConnection httpConn = (HttpURLConnection) url.openConnection(); httpConn.setConnectTimeout(10000); httpConn.setReadTimeout(10000); //待下载的文件长度,用于计算各个线程的起始/结束位置 int fileLen = httpConn.getContentLength(); System.out.println("文件大小是:" +fileLen +" byte"); //下载文件的线程个数 int threadNums = 5; int oneLen = fileLen/threadNums; int []startPos = new int[threadNums]; int []endPos = new int[threadNums]; for(int i=0;i<threadNums;i++) { startPos[i] = i*oneLen; endPos[i] = (i+1)*oneLen-1; } //重新设置最后一个线程的endPos endPos[threadNums-1] = fileLen - 1; //下载文件本地保存路径/名称 File downloadFile = new File("downloadFile.jpg"); //用于线程同步 CountDownLatch doneSignal = new CountDownLatch(threadNums); for(int i=0;i<threadNums;i++) { MultiDownloadThread mt = new MultiDownloadThread(url,doneSignal, downloadFile,startPos[i],endPos[i]); new Thread(mt).start(); } //等待线程下载完成 doneSignal.await(); System.out.println("下载完成...文件下载大小是" + downloadFile.length() +" byte"); } }
package io; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.URL; import java.util.concurrent.CountDownLatch; public class MultiDownloadThread implements Runnable { private URL url = null; private CountDownLatch doneSignal = null; private File file = null; private long startPos = 0; private long endPos = 0; private long curPos = 0;// 用于支持断点下载,未实现 public MultiDownloadThread(URL url, CountDownLatch doneSignal, File file, long startPos, long endPos) { this.url = url; this.doneSignal = doneSignal; this.file = file; this.startPos = startPos; this.endPos = endPos; } @Override public void run() { System.out.println("启动了一个下载线程...."); try { HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(1000); conn.setReadTimeout(1000); // 创建一个新的连接,设置下载的开始和结束位置 conn.setRequestProperty("Range", "bytes=" + startPos + "-" + endPos); // 如果请求部分资源 206 ok System.out.println("code=" + conn.getResponseCode()+", msg=" + conn.getResponseMessage()); RandomAccessFile raf = new RandomAccessFile(file, "rw"); raf.seek(startPos); InputStream os = conn.getInputStream(); byte[] buff = new byte[1024]; int len = 0; while ((len = os.read(buff)) != -1) { raf.write(buff, 0, len); } os.close(); raf.close(); doneSignal.countDown(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("线程下载完成...."); } }
参考:

浙公网安备 33010602011771号