一、多线程下载的原理、
将服务端的资源划分给成几个等分的块,分配给不同的线程同时执行下载。
划分方法 :
每个线程分配的长度为:int blocksize = length/n
每个线程的起止位置(i-1)*blocksize--->i*blocksize-1
最后一个线程结束位置为length
二、步骤:
1、在客户端创建与服务端同等大小的文件,使用RandomAccessFile类远程文件的设置操作
2.、开启几个线程,分配下载资源
3.、所有的线程执行完毕之后,文件下载完毕
三、java实现的多线程下载
1 package com.lewu.download; 2 3 import java.io.File; 4 import java.io.FileInputStream; 5 import java.io.FileOutputStream; 6 import java.io.InputStream; 7 import java.io.RandomAccessFile; 8 import java.net.HttpURLConnection; 9 import java.net.URL; 10 11 public class DownloadTest { 12 public static final String path = "http://192.168.1.118:8080/web/youdao.exe"; 13 public static int threadcount = 0; 14 15 public static void main(String[] args) throws Exception { 16 // 1.在客户端端创建一个相同大小的文件(获取服务器文件的大小) 17 18 URL url = new URL(path); 19 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 20 conn.setRequestMethod("GET"); 21 conn.setConnectTimeout(5000); 22 conn.setRequestProperty( 23 "User-Agent", 24 "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727)"); 25 int length = conn.getContentLength(); 26 System.err.println("文件的长度为"+length); 27 File file = new File("youdao.exe"); 28 RandomAccessFile randomfile = new RandomAccessFile(file, "rwd"); 29 randomfile.setLength(length); 30 31 // 创建三个线程 :线程分配下载资源 32 int blocksize = length / 3; 33 for (int i = 1; i <=3; i++) { 34 // 线程执行下载操作 ,需要定位下载的位置,则需要设置一些参数 35 int beginsize = (i - 1) * blocksize; 36 int endsize = i * blocksize - 1; 37 if (i == 3) { 38 endsize = length; 39 } 40 int id = i; 41 42 // 启动线程 43 new Thread(new DownloadThread(id, beginsize, endsize)).start(); 44 } 45 46 } 47 48 static class DownloadThread implements Runnable { 49 50 private int id; 51 private int beginsize; 52 private int endsize; 53 54 public DownloadThread(int id, int beginsize, int endsize) { 55 this.id = id; 56 this.beginsize = beginsize; 57 this.endsize = endsize; 58 } 59 60 @Override 61 public void run() { 62 try { 63 // 每一个线程在创建的时候 都创建一个名为id.text的文件用来保存下载进度 64 File idfile = new File("" + id + ".txt"); 65 if (idfile.exists()) { 66 // 将内容即进度读取出来 67 FileInputStream fin = new FileInputStream(idfile); 68 byte[] result = StreamTools.getBytes(fin); 69 String strinfo = new String(result); 70 if (!"".equals(strinfo) && strinfo != null) { 71 beginsize = Integer.parseInt(strinfo); 72 } 73 } 74 URL url = new URL(DownloadTest.path); 75 HttpURLConnection conn = (HttpURLConnection) url 76 .openConnection(); 77 78 conn.setRequestMethod("GET"); 79 conn.setConnectTimeout(5000); 80 if (beginsize > endsize) { 81 beginsize = endsize - 1; 82 } 83 conn.setRequestProperty( 84 "User-Agent", 85 "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727)"); 86 conn.setRequestProperty("Range", "bytes=" + beginsize + "-" 87 + endsize); 88 System.out.println("线程"+id+"从" + beginsize + "开始下载" + endsize + "结束"); 89 90 File file = new File("youdao.exe"); 91 RandomAccessFile randomfile = new RandomAccessFile(file, "rwd"); 92 // 如果下载被强行中断,这保存下载的位置, 93 randomfile.seek(beginsize); 94 95 InputStream in = conn.getInputStream(); 96 byte[] bt = new byte[1024]; 97 98 int len = 0; 99 int total = 0; 100 while ((len = in.read(bt)) != -1) { 101 randomfile.write(bt, 0, len); 102 // 下载量 下载位置 103 total += len; 104 // 将位置信息保存到相应的文件 105 FileOutputStream out = new FileOutputStream(idfile); 106 out.write((beginsize + total + "").getBytes()); 107 out.flush(); 108 out.close(); 109 110 } 111 in.close(); 112 randomfile.close(); 113 System.out.println("线程" + id + "执行完毕!"); 114 115 // 使用synchronized的代码块保count被多个线程同时访问 116 synchronized (DownloadTest.class) { 117 DownloadTest.threadcount++; 118 if (DownloadTest.threadcount >= 3) { 119 System.out.println("所有的线程都执行完毕了"); 120 for (int i = 0; i < 3; i++) { 121 File deleteFile = new File(i + ".txt"); 122 System.out.println(i + "删除" + deleteFile.delete()); 123 } 124 } 125 126 } 127 128 } catch (Exception e) { 129 e.printStackTrace(); 130 } 131 } 132 } 133 }
四、移植到Android上
功能:1、界面上有下载进度条,可以记录下载进度
2、设置暂停按钮(通过设置flag控制)
1 package com.lewu.download; 2 3 import java.io.File; 4 import java.io.FileInputStream; 5 import java.io.FileOutputStream; 6 import java.io.InputStream; 7 import java.io.RandomAccessFile; 8 import java.net.HttpURLConnection; 9 import java.net.URL; 10 import android.app.Activity; 11 import android.os.Bundle; 12 import android.os.Environment; 13 import android.view.View; 14 import android.view.View.OnClickListener; 15 import android.widget.Button; 16 import android.widget.EditText; 17 import android.widget.ProgressBar; 18 import android.widget.Toast; 19 20 public class MutiDownloadAndActivity extends Activity implements 21 OnClickListener { 22 private EditText down_edit; 23 private Button down_sure; 24 private Button down_stop; 25 private ProgressBar bar; 26 private int totalsize; 27 private boolean flag=true; 28 public static int threadcount = 0; 29 30 @Override 31 public void onCreate(Bundle savedInstanceState) { 32 super.onCreate(savedInstanceState); 33 setContentView(R.layout.main); 34 35 down_edit = (EditText) findViewById(R.id.down_edit); 36 down_sure = (Button) findViewById(R.id.down_sure); 37 down_stop = (Button) findViewById(R.id.down_stop); 38 bar = (ProgressBar) findViewById(R.id.progressBar1); 39 40 down_sure.setOnClickListener(this); 41 down_stop.setOnClickListener(this); 42 43 } 44 45 @Override 46 public void onClick(View v) { 47 switch (v.getId()) { 48 case R.id.down_sure: 49 50 String path = down_edit.getText().toString().trim(); 51 System.out.println(path + ">>>>>>>>>>"); 52 if ("".equals(path)) { 53 Toast.makeText(this, "路径不能为空", 0); 54 return; 55 } else { 56 down_stop.setClickable(true); 57 down_stop.setEnabled(true); 58 try { 59 URL url = new URL(path); 60 HttpURLConnection conn = (HttpURLConnection) url 61 .openConnection(); 62 conn.setRequestMethod("GET"); 63 conn.setConnectTimeout(5000); 64 conn.setRequestProperty( 65 "User-Agent", 66 "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727)"); 67 int length = conn.getContentLength(); 68 System.err.println("文件的长度为" + length); 69 //设置进度条的进度 70 bar.setMax(length); 71 File file = new File( 72 Environment.getExternalStorageDirectory(), 73 getFileName(path)); 74 RandomAccessFile randomfile = new RandomAccessFile(file, 75 "rwd"); 76 randomfile.setLength(length); 77 78 // 创建三个线程 :线程分配下载资源 79 int blocksize = length / 3; 80 for (int i = 1; i <= 3; i++) { 81 // 线程执行下载操作 ,需要定位下载的位置,则需要设置一些参数 82 int beginsize = (i - 1) * blocksize; 83 int endsize = i * blocksize - 1; 84 if (i == 3) { 85 endsize = length; 86 } 87 int id = i; 88 89 // 启动线程 90 new Thread(new DownloadThread(id, beginsize, endsize, 91 path)).start(); 92 93 } 94 } catch (Exception e) { 95 e.printStackTrace(); 96 } 97 break; 98 } 99 100 case R.id.down_stop: 101 flag = false; 102 down_stop.setClickable(true); 103 down_stop.setEnabled(true); 104 105 break; 106 107 } 108 } 109 110 class DownloadThread implements Runnable { 111 112 private int id; 113 private int beginsize; 114 private int endsize; 115 private String path; 116 117 public DownloadThread(int id, int beginsize, int endsize, String path) { 118 this.id = id; 119 this.beginsize = beginsize; 120 this.endsize = endsize; 121 this.path = path; 122 } 123 124 @Override 125 public void run() { 126 try { 127 flag = true; 128 // 每一个线程在创建的时候 都创建一个名为id.text的文件用来保存下载进度 129 File idfile = new File("/mnt/sdcard/" + id + ".txt"); 130 if (idfile.exists()) { 131 // 将内容即进度读取出来 132 FileInputStream fin = new FileInputStream(idfile); 133 byte[] result = StreamTools.getBytes(fin); 134 String strinfo = new String(result); 135 if (!"".equals(strinfo) && strinfo != null) { 136 beginsize = Integer.parseInt(strinfo); 137 } 138 } 139 URL url = new URL(path); 140 HttpURLConnection conn = (HttpURLConnection) url 141 .openConnection(); 142 143 conn.setRequestMethod("GET"); 144 conn.setConnectTimeout(5000); 145 if (beginsize > endsize) { 146 beginsize = endsize - 1; 147 } 148 conn.setRequestProperty( 149 "User-Agent", 150 "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727)"); 151 conn.setRequestProperty("Range", "bytes=" + beginsize + "-" 152 + endsize); 153 System.out.println("线程" + id + "从" + beginsize + "开始下载" 154 + endsize + "结束"); 155 156 File file = new File(Environment.getExternalStorageDirectory(), 157 getFileName(path)); 158 RandomAccessFile randomfile = new RandomAccessFile(file, "rwd"); 159 // 如果下载被强行中断,这保存下载的位置, 160 randomfile.seek(beginsize); 161 162 InputStream in = conn.getInputStream(); 163 byte[] bt = new byte[1024]; 164 165 int len = 0; 166 int total = 0; 167 while ((len = in.read(bt)) != -1) { 168 randomfile.write(bt, 0, len); 169 // 下载量 下载位置 170 total += len; 171 172 // 设置进度条的进度 173 setProgressbarProgress(len); 174 175 // 将位置信息保存到相应的文件 176 FileOutputStream out = new FileOutputStream(idfile); 177 out.write((beginsize + total + "").getBytes()); 178 out.flush(); 179 out.close(); 180 if(!flag){ 181 return; 182 } 183 184 } 185 in.close(); 186 randomfile.close(); 187 System.out.println("线程" + id + "执行完毕!"); 188 189 // 使用synchronized的代码块保count被多个线程同时访问 190 synchronized (MutiDownloadAndActivity.class) { 191 MutiDownloadAndActivity.threadcount++; 192 if (MutiDownloadAndActivity.threadcount >= 3) { 193 System.out.println("所有的线程都执行完毕了"); 194 for (int i = 1; i <= 3; i++) { 195 File deleteFile = new File("/mnt/sdcard/" + i 196 + ".txt"); 197 System.out.println(i + "删除" + deleteFile.delete()); 198 } 199 } 200 201 } 202 203 } catch (Exception e) { 204 e.printStackTrace(); 205 } 206 } 207 } 208 209 public static String getFileName(String path) { 210 int index = path.lastIndexOf("/") + 1; 211 return path.substring(index); 212 213 } 214 215 public void setProgressbarProgress(int len) { 216 if (len != 0) { 217 totalsize += len; 218 bar.setProgress(totalsize); 219 } 220 221 } 222 223 }
下载界面: