多线程下载业务逻辑:html
一、URL请求获取下载文件的大小、计算每一个线程下载的起始位置java
二、RandomAccessFile类在存储空间占位,随机访问流在多线程下可同时读写文件api
三、开启线程,每一个线程负责下载文件的一部分,最后由随机访问流自动拼接文件,合成服务器
四、启动线程多线程
此项目须要了解java.io.RandomAccessFile、java.net .HttpURLConnection 的具体用法,在线API参考文档【http://tool.oschina.net/apidocs/apidoc?api=jdk-zh】dom
java.lang.Object java.net.URLConnection java.net.HttpURLConnection
重点:HttpURLConnection的父类URLConnection有个很是重要的 setRequestProperty() 方法,如下连接介绍了有关多线程下载,Range请求头的使用例子。ide
详情点击:【http://www.tuicool.com/articles/viUfMjY】ui
import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; public class Demo { //定义全局变量address static String address = "http://localhost:8080/ThreadDownloadServer/Blood.mp4"; public static void main(String[] args) { //开启三个线程下载 downloadFile(3); } /** * 功能:计算每一个线程下载的起始位置,并执行下载。 * @param 线程数 */ public static void downloadFile(int threadCount){ try { URL url = new URL(address); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(5000); conn.setReadTimeout(5000); //获取文件字节总大小 int fileLength = conn.getContentLength(); System.out.println("文件总大小:"+fileLength/1024/1024+"MB"); int code = conn.getResponseCode(); if (code == 200) { //分配每一个线程"平均"下载字节数 int blockSize = fileLength/threadCount; //建立随机访问流,在本地为文件占位 RandomAccessFile raf = new RandomAccessFile("Blood.mp4", "rw"); //重点:假设文件只有10字节,分3个线程来下载,理解理解for循环代码 for (int threadId = 0; threadId < threadCount; threadId++) { int startIndex = threadId*blockSize; int endIndex = (threadCount+1)*blockSize-1; if (threadId == threadCount) { endIndex = fileLength - 1; } //每循环一次,开启一个线程执行下载,别漏了start()。 new ThreadDownload(threadId, startIndex, endIndex).start();; } conn.disconnect(); }else{ System.out.println("请求失败,服务器响应码:"+code); } } catch (MalformedURLException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * 功能:开启线程下载,下载分配好的文件长度 * @author Administrator * @param 线程ID,下载开始位置,结束位置, */ public static class ThreadDownload extends Thread{ private int threadId; private int startIndex; private int endIndex; //构造方法接收3个参数初始化局部变量:线程ID、开始位置、结束位置 public ThreadDownload(int threadId, int startIndex, int endIndex) { this.threadId = threadId; this.startIndex = startIndex; this.endIndex = endIndex; } @Override public void run(){ try { URL url = new URL(address); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(5000); conn.setReadTimeout(5000); System.out.println("线程"+threadId+"下载开始位置"+startIndex+" 结束位置:"+endIndex); //设置请求头,请求部分资源下载 conn.setRequestProperty("Range", "bytes:"+startIndex+"-"+endIndex); //服务器响应码206表示请求部分资源成功 if (conn.getResponseCode()==206) { InputStream inputStream = conn.getInputStream(); RandomAccessFile raf = new RandomAccessFile(new File("Blood.mp4"), "rw"); //查找写入开始位置 raf.seek(startIndex); byte array[] = new byte[1024]; int len = -1; while((len = inputStream.read(array))!=-1){ raf.write(array, 0, len); } inputStream.close(); raf.close(); conn.disconnect(); }else{ System.out.println("服务器响应码:"+conn.getResponseCode()); } } catch (MalformedURLException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
刷新项目,可见项目已生成:this