1.基本原理
先使用head
方法查询得到对应文件的Content-Length
,然后拆分成多个部分,交由多个线程去处理,使用"Range", "bytes=" + start + "-" + end
这个header来指定下载文件的哪个部分。
2.代码实现
为了方便展示,我是用了一个类来实现,其余都是内部类
/** * Created by yangzheng03 on 2018/1/16. * https://www.dubby.cn/ */public class DownloadTool { private static String prefix = String.valueOf(System.currentTimeMillis()); private static String path = ""; public static void main(String[] args) { long startTimestamp = System.currentTimeMillis(); if (args == null || args.length < 1) { System.out.println("please input the file url."); return; } final String urlString = args[0]; if (args.length >= 2) { path = args[1]; } int number = 10; if (args.length >= 3) { number = Integer.parseInt(args[2]); } System.out.println("Download started, url is \"" + urlString + "\""); ExecutorService threadPool = Executors.newFixedThreadPool(number); try { URL url = new URL(urlString); HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection(); httpURLConnection.setRequestMethod("HEAD"); String contentLengthStr = httpURLConnection.getHeaderField("Content-Length"); long contentLength = Long.parseLong(contentLengthStr); if (contentLength > 1024 * 1024) { System.out.println("Content-Length\t" + (contentLength / 1024 / 1024) + "MB"); } else if (contentLength > 1024) { System.out.println("Content-Length\t" + (contentLength / 1024) + "KB"); } else { System.out.println("Content-Length\t" + (contentLength) + "B"); } long tempLength = contentLength / number; long start = 0, end = -1; Map<Integer, Future<DownloadTemp>> futureMap = new HashMap<>(number); for (int i = 0; i < number; ++i) { start = end + 1; end = end + tempLength; if (i == number - 1) { end = contentLength; } System.out.println("start:\t" + start + "\tend:\t" + end); DownloadThread thread = new DownloadThread(i, start, end, urlString); futureMap.put(i, threadPool.submit(thread)); } System.out.println(); String filename = urlString.substring(urlString.lastIndexOf("/") + 1); if (!path.equals("")) { filename = path + "/" + filename; } RandomAccessFile resultFile = new RandomAccessFile(filename, "rw"); for (int i = 0; i < number; ++i) { Future<DownloadTemp> future = futureMap.get(i); DownloadTemp temp = future.get(); RandomAccessFile tempFile = new RandomAccessFile(temp.filename, "r"); tempFile.getChannel().transferTo(0, tempFile.length(), resultFile.getChannel()); } threadPool.shutdown(); } catch (Exception e) { e.printStackTrace(); } long completedTimestamp = System.currentTimeMillis(); System.out.println(); System.out.println("cost " + (completedTimestamp - startTimestamp) / 1000 + "s"); } private static class DownloadThread implements Callable<DownloadTemp> { private int index; private String filename; private long start, end; private String urlString; DownloadThread(int index, long start, long end, String url) { this.urlString = url; this.index = index; this.start = start; this.end = end; if (path.equals("")) { this.filename = prefix + "-temp-" + index; } else { this.filename = path + "/" + prefix + "-temp-" + index; } } @Override public DownloadTemp call() throws Exception { URL url = new URL(urlString); HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection(); httpURLConnection.setRequestMethod("GET"); httpURLConnection.setRequestProperty("Range", "bytes=" + start + "-" + end); InputStream inputStream = null; OutputStream outputStream = null; try { inputStream = httpURLConnection.getInputStream(); File file = new File(filename); outputStream = new FileOutputStream(file); while (true) { byte[] bytes = new byte[10240]; int length = inputStream.read(bytes); if (length <= 0) { break; } outputStream.write(bytes, 0, length); } outputStream.flush(); } finally { if (inputStream != null) { inputStream.close(); } if (outputStream != null) { outputStream.close(); } } DownloadTemp downloadTemp = new DownloadTemp(); downloadTemp.index = index; downloadTemp.filename = filename; System.out.println("thread\t" + index + "\tcompleted."); return downloadTemp; } } private static class DownloadTemp { private int index; private String filename; } }
3.使用Jar包
下载下来后,改名为download-1.0.jar
,执行:
java -jar download-1.0.jar https://blog.dubby.cn/upload/2018-01-13/68b780a2-1a6c-49dc-914e-0469cb969471.zip /Users/test/Desktop/download 15
其中有三个参数:
下载的URL,必填
下载到目标目录,选填,如果不指定,下载到当前目前下
开启的线程数,选填,默认是10个线程
测试结果:
target java -jar download-1.0.jar https://blog.dubby.cn/upload/2018-01-13/68b780a2-1a6c-49dc-914e-0469cb969471.zip /Users/test/Desktop/download 15 Download started, url is "https://blog.dubby.cn/upload/2018-01-13/68b780a2-1a6c-49dc-914e-0469cb969471.zip"Content-Length 1MBstart: 0 end: 83780start: 83781 end: 167561start: 167562 end: 251342start: 251343 end: 335123start: 335124 end: 418904start: 418905 end: 502685start: 502686 end: 586466start: 586467 end: 670247start: 670248 end: 754028start: 754029 end: 837809start: 837810 end: 921590start: 921591 end: 1005371start: 1005372 end: 1089152start: 1089153 end: 1172933start: 1172934 end: 1256726thread 0 completed. thread 10 completed. thread 1 completed. thread 9 completed. thread 14 completed. thread 11 completed. thread 7 completed. thread 6 completed. thread 2 completed. thread 4 completed. thread 3 completed. thread 8 completed. thread 12 completed. thread 13 completed. thread 5 completed. cost 9s
image
作者:我是杨正
链接:https://www.jianshu.com/p/9f50364034ea