首页 > 代码库 > java中多线程下载
java中多线程下载
多线程下载可以抢占其它相同优先级用户的网络资源(宽带),所以说下载速度比较快,迅雷、快播都使用了多线程下载。
1.请求服务器上的文件的长度
2.根据服务器上的文件长度在手机上创建一个一模一样大小的文件
3.根据线程的个数和文件的长度来计算每一个线程需要下载的范围
文件的长度为:10 线程的数量为:3 每一块的大小:10/3=3.3333=3
线程0:0~2
线程1:3~5
线程2:6~9
开始的位置:线程的id*每一块的大小。
结束的位置:线程的(id+1 ) *每一块的大小 -1;
最后一个线程结束的位置是:文件的长度-1;
4.开启多个线程,每一个线程下载自己的那块范围
1).请求服务器上传的部分内容。
Range(请求头): bytes=3-8获取服务器文件3-8的内容
特点:服务器返回值不再是200,是206.
2).把写入的位置移动到线程开始的位置上。
3).循环的读取内容写入到本地。
####断点下载(非正常关闭)
*在每一次读取到数据,写入本地数据时,应该把总记录存到本地。
*当下一次开始下载时,先查看有没有下载的记录(存档),如果有就取出来,继续下载
代码:
public class DownloadUtils { // 完成的线程 private int completeThread; private int threadCount; /** * 开始多线程下载 * * @param url * 访问的服务器地址 * @param dir * 本地接收路径 * @param threadCount * 开启多少条线程下载 */ public void startMultiThreadDownload(final String url, final String dir, final int threadCount) { this.threadCount = threadCount; completeThread = 0; new Thread(new Runnable() { @Override public void run() { // 1.请求服务器上的文件的长度 int fileLength = getRemoteServiceFileLength(url); if (fileLength == -1) { System.out.println("请求服务器文件长度失败,停止下载"); return; } System.out.println("远程服务器的长度为:" + fileLength); // 2.根据服务器上的文件长度在手机上创建一个一模一样大小的文件 String fileName = url.substring(url.lastIndexOf("/") + 1); File file = new File(dir, fileName); try { RandomAccessFile raf = new RandomAccessFile(file, "rwd"); raf.setLength(fileLength); raf.close(); System.out.println("本地文件创建成功" + file.getPath()); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } // 3.根据线程的个数和文件的长度来计算每一个线程需要下载的范围 int blockSize = fileLength / threadCount; for (int i = 0; i < threadCount; i++) { // 开始的位置:线程的id*每一块的大小。 int start = i * blockSize; // 结束的位置:线程的(id+1 ) *每一块的大小 -1; int end = (i + 1) * blockSize - 1; // 最后一个线程结束的位置是:文件的长度-1; if ((i == threadCount - 1)) { end = fileLength - 1; } // 4.开启多个线程,每一个线程下载自己的那块范围,传递参数:下载地址,存储文件的路径 // 开始的位置,结束的位置,线程ID new Thread(new DownloadRunnable(url, file.getPath(), start, end, i)).start(); } } }).start(); } /** * 下载任务类 * * @author Administrator * */ class DownloadRunnable implements Runnable { private String url; private File localFile; // 文件的路径 private int start; // 当前线程开始下载的位置 private int end; // 结束下载的位置 private int threadID;// 线程ID public DownloadRunnable(String url, String path, int start, int end, int i) { this.url = url; this.localFile = new File(path); this.start = start; this.end = end; this.threadID = i; } @Override public void run() { HttpURLConnection conn = null; try { // 添加断点下载 int total = 0;// 当前线程下载的总进度 // 把总进度值存储到本地去 File cacheFile = new File(localFile.getParent(), "thread" + threadID + ".hm"); RandomAccessFile cacheRAF = new RandomAccessFile(cacheFile, "rwd"); // 判断上一次是否有缓存的进度 if (cacheFile.exists() && cacheFile.length() > 0) {// 文件存在并且有数据 // 上一次下载的进度 int progress = Integer.valueOf(cacheRAF.readLine()); start += progress;// 把上一次的进度在开始的位置加上,继续下载 total = progress;// 把上一次下载总进度赋值 } System.out.println("线程" + threadID + ",开始下载了,下载的范围是:" + start + " ~" + end); conn = (HttpURLConnection) new URL(url).openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(5000); conn.setReadTimeout(5000); // 设置请求头消息:Range,请求服务器上文件的部分内容 conn.setRequestProperty("Range", "bytes=" + start + "-" + end); int responseCode = conn.getResponseCode(); if (responseCode == 206) { int contentLength = conn.getContentLength(); System.out.println("线程" + threadID + ",请求成功,内容长度为:" + contentLength); // 得到服务器返回的数据 InputStream is = conn.getInputStream(); RandomAccessFile raf = new RandomAccessFile(localFile, "rwd"); // 把写入的位置移动到线程开始的位置上 raf.seek(start); byte[] buf = new byte[1024]; int len = 0; while ((len = is.read(buf)) != -1) { raf.write(buf, 0, len); total += len; cacheRAF.seek(0);// 每一次都移动到文件的开始位置进行写入 cacheRAF.write(String.valueOf(total).getBytes()); } cacheRAF.close(); raf.close(); is.close(); System.out.println("线程" + threadID + ",下载完成了."); completeThread++; if (completeThread == threadCount) { System.out.println("全部下载完整,删除临时配置文件."); for (int i = 0; i < threadCount; i++) { File deleteFile = new File(localFile.getParent(), "thread" + i + ".hm"); System.out.println(deleteFile.delete()); } } } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { if (conn != null) { conn.disconnect(); } } } } /** * 根据URL连接请求远程服务器获取文件大小 * * @param url * 服务器地址 * @return */ private int getRemoteServiceFileLength(String url) { HttpURLConnection conn = null; try { conn = (HttpURLConnection) new URL(url).openConnection(); // 请求方式,必须大小 conn.setRequestMethod("GET"); // 连接超时 conn.setConnectTimeout(5000); // 读取超时 conn.setReadTimeout(5000); int reponseCode = conn.getResponseCode();// 获取的服务器返回的响应码 if (reponseCode == 200) { return conn.getContentLength(); } } catch (Exception e) { e.printStackTrace(); } finally { if (conn != null) { conn.disconnect(); } } return -1; } }
调用:
public class Demo { public static void main(String[] args) { System.out.println("开启多线程下载了............"); // 链接,路径,线程的数量 String url = "http://192.168.1.109/FeiQ.exe"; String dir = "F:/WDJDownload"; int threadCount = 3; DownloadUtils du = new DownloadUtils(); du.startMultiThreadDownload(url, dir, threadCount); } }
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。