10_多线程下载_完成
接下来要创建多个线程了。
在服务端这边要通过一个请求头Range,用setRequestProperty给它传一个Range.每一个线程下载的起始位置和结束位置都是不一样的。但是正好是连在一起的。
package com.itheima.multiThreadDownload; //import java.net.MalformedURLException; import java.io.InputStream; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; //import java.net.URLConnection; import java.net.URLConnection; public class MultiThreadDownload { private static String path = "http://127.0.0.1:8080/FeiQ.exe"; private static int threadCount= 3;//不搞那么多就搞三个线程. public static void main(String[] args) { //①联网获取要下载的文件长度 try { URL url = new URL(path); //URLConnection openConnection = url.openConnection(); HttpURLConnection openConnection = (HttpURLConnection) url.openConnection(); openConnection.setRequestMethod("GET"); openConnection.setConnectTimeout(10000); int responseCode = openConnection.getResponseCode(); if(responseCode==200){ //获取要下载的文件长度 //int contentLength = openConnection.getContentLength(); //long contentLengthLong = openConnection.getContentLengthLong(); int contentLength = openConnection.getContentLength(); //在本地创建一个一样的文件 RandomAccessFile file = new RandomAccessFile(getFilename(path), "rw");//第一个叫file或者说是name(路径),第二个参数叫mode //名字可以通过路径去获取,截取这个路径最后一个斜杠.剩下的这个就是我要下载的文件名 //file.setLength(contentLengthLong);//文件创建出来之后去设置文件的长度 file.setLength(contentLength);//文件创建出来之后去设置文件的长度 //计算每一个线程要下载多少数据 //int blockSize = contentLengthLong/threadCount; int blockSize = contentLength/threadCount; //计算每一个线程要下载的数据范围 for (int i = 0; i < threadCount; i++) { //用i和blockSize来确定startIndex和endIndex int startIndex = i*blockSize; int endIndex = (i+1)*blockSize-1; if(i==threadCount-1){ //说明是最后一个线程 endIndex = contentLength-1; } new DownLoadThread(startIndex, endIndex, i).run();//main是静态的方法,要求DownLoadThread也是静态的. } } } //catch (MalformedURLException e) { catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } private static class DownLoadThread extends Thread{ private int startIndex; private int endIndex; private int threadID;//线程的编号 public DownLoadThread(int startIndex, int endIndex, int threadID) { super(); this.startIndex = startIndex; this.endIndex = endIndex; this.threadID = threadID; } @Override public void run() { // TODO Auto-generated method stub //super.run(); //run方法还是要联网,拿着url向服务端请求数据.多个线程联网下载数据 try { URL url = new URL(path); //URLConnection openConnection = url.openConnection(); HttpURLConnection openConnection = (HttpURLConnection) url.openConnection(); openConnection.setRequestMethod("GET"); openConnection.setConnectTimeout(10000); //设置Range头,用计算好的开始索引和结束索引到服务端请求数据 openConnection.setRequestProperty("Range", "bytes="+startIndex+"-"+endIndex);//计算出来的开始索引和结束的位置 if(openConnection.getResponseCode()==206){ System.out.println("线程"+threadID+"开始下载"+startIndex); InputStream inputStream = openConnection.getInputStream(); //通过RandomAccessFile来写对应的内容了 int len = -1; byte[] buffer = new byte[1024]; RandomAccessFile file = new RandomAccessFile(getFilename(path), "rw");//文件存在的话它其实是做打开的操作,文件不存在的话是做创建 //这个文件咱们已经创建好了,现在咱们是要给它打开. //打开之后需要注意这一步一定不要忘记 要 要 seek到startIndex位置 写入数据 //如果你这个seek忘了 实际上你每一个线程都是从头开始写的 三个线程都是从最开始写到了三分之一的位置 //把这三件数据写到了同一个位置 你的文件大小 因为咱们之前创建了一个相同大小的文件 你看起来这个大小是没问题的 但是只有前三分之一有数据 file.seek(startIndex);//挪到每个线程指定的位置开始去写 while((len=inputStream.read(buffer))!=-1){//写对应的内容了. file.write(buffer,0,len); }//整个while循环结束了这个文件就写完了 file.close(); System.out.println("线程"+threadID+"下载结束"); } //} catch (MalformedURLException e) { } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } private static String getFilename(String path) { String[] split = path.split("/");//拿斜杠去切 return split[split.length-1]; } }