代码地址:https://github.com/gcWpengfei/spring-cloud-rsa-aes-demo/blob/master/aes-rsa-util/src/main/java/com/wpf/util/down/MutiDownloadTest1.java
多线程下载原理
- 客户端要下载一个文件, 首先请求服务器,服务器将这个文件传送给客户端,客户端保存到本地, 完成了一个下载的过程.
- 多线程下载的思想是客户端开启多个线程同时下载,每个线程只负责下载文件的一部分, 当所有线程下载完成的时候,文件下载完毕.
- 并不是线程越多下载越快, 与网络环境有很大的关系
- 在同等的网络环境下,多线程下载速度要高于单线程.
- 多线程下载占用资源比单线程多,相当于用资源换取速度
java代码实现多线程下载
代码的思路:
- 首先要获取要下载文件的大小
- 在磁盘上使用RandomAccessFile 这个类在磁盘上创建一个大小一样的文件,将来将数据写入这个文件.
- 为每个线程分配下载任务. 内容包括线程现在文件的开始位置和结束位置.这里面有一点数学知识,代码中有备注.
- 启动下载线程
- 判断有没有保存上次下载的临时文件.
- 在启动线程下载的时候保存下载的位置信息
- 下载完毕后删除当前线程产生的临时文件
网上找到断点下载代码有问题,就重新手写了一个
public class MutiDownloadTest1 { private static final int THREAD_COUNT = 5; private static final String DOWNLOAD_URL = "http://down.360safe.com/se/360se9.1.0.426.exe"; private static final String fileName = "F:\\1\\test\\2/360.exe"; static final String filePath = "F:\\1\\test\\2/"; public static void main(String[] args) { long fileSize = 0; HttpURLConnection connection = null; try { connection = (HttpURLConnection)new URL(DOWNLOAD_URL).openConnection(); connection.setRequestMethod("GET"); connection.setConnectTimeout(8000); connection.setReadTimeout(8000); /** * 当请求成功时,返回http状态码200 */ if (connection.getResponseCode() == 200) { /** * 打开一个RandomAccessFile文件,打开方式为读写(rw) * setLength是先在存储设备占用一块空间,防止下载到一半空间不足 */ RandomAccessFile randomAccessFile = new RandomAccessFile(fileName , "rw"); fileSize = connection.getContentLength(); randomAccessFile.setLength(fileSize); randomAccessFile.close(); long eachSize = fileSize/THREAD_COUNT; for(int i=0; i<THREAD_COUNT; i++){ long startIndex = i*eachSize; long endIndex = (i+1)*eachSize - 1; /** * 当时最后一个线程的时候,endIndex的值就由文件大小 */ if(i == THREAD_COUNT - 1){ endIndex = fileSize; } Runnable runnable = new DownloadThreadTest1(DOWNLOAD_URL,fileName,i, startIndex, endIndex); new Thread(runnable).start(); } } } catch (IOException e) { e.printStackTrace(); // log.info(ExceptionUtils.getFullStackTrace(e)); } finally { if(null != connection){ connection.disconnect(); } } } } class DownloadThreadTest1 implements Runnable { // private static final Logger log = Logger.getLogger(DownloadThreadTest.class); private String url; private String fileName; private int threadId; private long startIndex; private long endIndex; private HttpURLConnection httpURLConnection; private RandomAccessFile randomAccessFile; private InputStream inputStream; public DownloadThreadTest1(String url, String fileName, int threadId, long startIndex, long endIndex) { super(); this.url = url; this.fileName = fileName; this.threadId = threadId; this.startIndex = startIndex; this.endIndex = endIndex; } @Override public void run() { RandomAccessFile downThreadStream = null; /* * 查看临时文件 */ File downThreadFile = new File(MutiDownloadTest1.filePath, "wpf_thread_"+threadId+".dt"); try { if(downThreadFile.exists()){ downThreadStream = new RandomAccessFile(downThreadFile,"rwd"); String startIndex_str = downThreadStream.readLine(); if(null == startIndex_str || "".equals(startIndex_str)){ } else { this.startIndex = Long.parseLong(startIndex_str) - 1; // //设置下载起点 } } else { downThreadStream = new RandomAccessFile(downThreadFile, "rwd"); } httpURLConnection = (HttpURLConnection) new URL(url + "?ts=" + System.currentTimeMillis()).openConnection(); httpURLConnection.setRequestMethod("GET"); httpURLConnection.setConnectTimeout(8000); httpURLConnection.setReadTimeout(8000); /** * 设置请求范围. */ httpURLConnection.setRequestProperty("RANGE", "bytes=" + startIndex + "-" + endIndex); /** * 当请求部分数据成功的时候,返回http状态码206 */ if (httpURLConnection.getResponseCode() == 206){ inputStream = httpURLConnection.getInputStream(); randomAccessFile = new RandomAccessFile(fileName, "rwd"); /** * 把开始写的位置设置为startIndex,与请求数据的位置一致 */ randomAccessFile.seek(startIndex); byte[] bytes = new byte[1024]; int len; int total = 0; while((len = inputStream.read(bytes)) != -1){ total += len; //log.info("线程" + threadId + ":" + total); System.out.println("线程: " + threadId + ":" + total); randomAccessFile.write(bytes, 0, len); /* * 将当前现在到的位置保存到文件中 */ downThreadStream.seek(0); downThreadStream.write((startIndex + total + "").getBytes("UTF-8")); } } } catch (IOException e) { e.printStackTrace(); // log.info(ExceptionUtils.getFullStackTrace(e)); } finally { try { if(null != downThreadStream){ downThreadStream.close(); } if(null != httpURLConnection){ httpURLConnection.disconnect(); } if(null != inputStream){ inputStream.close(); } if(null != randomAccessFile){ randomAccessFile.close(); } cleanTemp(downThreadFile); } catch (Exception e) { e.printStackTrace(); //log.info(ExceptionUtils.getFullStackTrace(e)); } } } //删除线程产生的临时文件 private void cleanTemp(File file){ file.delete(); }