多线程下载文件的过程是:
(1)首先获得下载文件的长度,然后设置本地文件的长度。
HttpURLConnection.getContentLength();//获取下载文件的长度
RandomAccessFile file = new RandomAccessFile("QQSetup.exe","rwd");
file.setLength(filesize);//设置本地文件的长度
(2)根据文件长度和线程数计算每条线程下载的数据长度和下载位置。
如:文件的长度为6M,线程数为3,那么,每条线程下载的数据长度为2M,每条线程开始下载的位置如下图所示。
例如10M大小,使用3个线程来下载,
线程下载的数据长度 (10%3 == 0 ? 10/3:10/3+1) ,第1,2个线程下载长度是4M,第三个线程下载长度为2M
下载开始位置:线程id*每条线程下载的数据长度 = ?
下载结束位置:(线程id+1)*每条线程下载的数据长度-1=?
(3)使用Http的Range头字段指定每条线程从文件的什么位置开始下载,下载到什么位置为止,
如:指定从文件的2M位置开始下载,下载到位置(4M-1byte)为止
代码如下:HttpURLConnection.setRequestProperty("Range", "bytes=2097152-4194303");
(4)保存文件,使用RandomAccessFile类指定每条线程从本地文件的什么位置开始写入数据。
RandomAccessFile threadfile = new RandomAccessFile("QQWubiSetup.exe ","rwd");
threadfile.seek(2097152);//从文件的什么位置开始写入数据
[java] view plain copy
- /**
- *多线程下载,UI更新类
- *@author young
- * */
- public class MultiThreadDownload extends Thread{
- private static final String TAG = "MultiThreadDownload";
- /**每一个线程需要下载的大小 */
- private int blockSize;
- /*** 线程数量<br> 默认为5个线程下载*/
- private int threadNum = 5;
- /*** 文件大小 */
- private int fileSize;
- /** * 已经下载多少 */
- private int downloadSize;
- /**文件的url,线程编号,文件名称*/
- private String UrlStr,ThreadNo,fileName;
- /***保存的路径*/
- private String savePath;
- /**下载的百分比*/
- private int downloadPercent = 0;
- /**下载的 平均速度*/
- private int downloadSpeed = 0;
- /**下载用的时间*/
- private int usedTime = 0;
- /**当前时间*/
- private long curTime;
- /**是否已经下载完成*/
- private boolean completed = false;
- private Handler handler ;
- /**
- * 下载的构造函数
- * @param url 请求下载的URL
- * @param handler UI更新使用
- * @param savePath 保存文件的路径
- */
- public MultiThreadDownload(Handler handler,String url,String savePath)
- {
- this.handler = handler;
- this.UrlStr = url;
- this.savePath = savePath;
- Log.e(TAG, toString());
- }
- @Override
- public void run() {
- FileDownloadThread[] fds = new FileDownloadThread[threadNum];//设置线程数量
- try {
- URL url = new URL(UrlStr);
- URLConnection conn = url.openConnection();
- fileSize = conn.getContentLength();
- this.fileName = FileUtil.getFileName(UrlStr);
- //只创建一个文件,saveFile下载内容
- File saveFile = new File(savePath+"/"+fileName);
- Log.e(TAG, "文件一共:"+fileSize+" savePath "+savePath+" fileName "+fileName);
- RandomAccessFile accessFile = new RandomAccessFile(saveFile,"rwd");
- //设置本地文件的长度和下载文件相同
- accessFile.setLength(fileSize);
- accessFile.close();
- //Handler更新UI,发送消息
- sendMsg(FileUtil.startDownloadMeg);
- //每块线程下载数据
- blockSize = ((fileSize%threadNum)==0)?(fileSize/threadNum):(fileSize/threadNum+1);
- Log.e(TAG, "每个线程分别下载 :"+blockSize);
- for (int i = 0; i < threadNum; i++) {
- int curThreadEndPosition = (i+1)!=threadNum ? ((i+1)*blockSize-1) : fileSize;
- FileDownloadThread fdt = new FileDownloadThread(url, saveFile, i*blockSize, curThreadEndPosition);
- fdt.setName("thread"+i);
- fdt.start();
- fds[i]=fdt;
- }
- /**
- * 获取数据,更新UI,直到所有下载线程都下载完成。
- */
- boolean finished = false;
- //开始时间,放在循环外,求解的usedTime就是总时间
- long startTime = System.currentTimeMillis();
- while(!finished)
- {
- downloadSize = 0;
- finished = true;
- for (int i = 0; i < fds.length; i++) {
- downloadSize+= fds[i].getDownloadSize();
- if(!fds[i].isFinished())
- {
- finished = false;
- }
- }
- downloadPercent = (downloadSize*100)/fileSize;
- curTime = System.currentTimeMillis();
- System.out.println("curTime = "+curTime+" downloadSize = "+downloadSize+" usedTime "+(int) ((curTime-startTime)/1000));
- usedTime = (int) ((curTime-startTime)/1000);
- if(usedTime==0)usedTime = 1;
- downloadSpeed = (downloadSize/usedTime)/1024;
- sleep(1000);/*1秒钟刷新一次界面*/
- sendMsg(FileUtil.updateDownloadMeg);
- }
- Log.e(TAG, "下载完成");
- completed = true;
- sendMsg(FileUtil.endDownloadMeg);
- } catch (Exception e) {
- Log.e(TAG, "multi file error Exception "+e.getMessage());
- e.printStackTrace();
- }
- super.run();
- }
- /**
- * 得到文件的大小
- * @return
- */
- public int getFileSize()
- {
- return this.fileSize;
- }
- /**
- * 得到已经下载的数量
- * @return
- */
- public int getDownloadSize()
- {
- return this.downloadSize;
- }
- /**
- * 获取下载百分比
- * @return
- */
- public int getDownloadPercent(){
- return this.downloadPercent;
- }
- /**
- * 获取下载速度
- * @return
- */
- public int getDownloadSpeed(){
- return this.downloadSpeed;
- }
- /**
- * 修改默认线程数
- * @param threadNum
- */
- public void setThreadNum(int threadNum){
- this.threadNum = threadNum;
- }
- /**
- * 分块下载完成的标志
- * @return
- */
- public boolean isCompleted(){
- return this.completed;
- }
- @Override
- public String toString() {
- return "MultiThreadDownload [threadNum=" + threadNum + ", fileSize="
- + fileSize + ", UrlStr=" + UrlStr + ", ThreadNo=" + ThreadNo
- + ", savePath=" + savePath + "]";
- }
- /**
- * 发送消息,用户提示
- * */
- private void sendMsg(int what)
- {
- Message msg = new Message();
- msg.what = what;
- handler.sendMessage(msg);
- }
[java] view plain copy
- public class FileDownloadThread extends Thread{
- private static final String TAG = "FileDownloadThread";
- /**缓冲区 */
- private static final int BUFF_SIZE = 1024;
- /**需要下载的URL*/
- private URL url;
- /**缓存的FIle*/
- private File file;
- /**开始位置*/
- private int startPosition;
- /**结束位置*/
- private int endPosition;
- /**当前位置*/
- private int curPosition;
- /**完成*/
- private boolean finished = false;
- /**已经下载多少*/
- private int downloadSize = 0;
- /***
- * 分块文件下载,可以创建多线程模式
- * @param url 下载的URL
- * @param file 下载的文件
- * @param startPosition 开始位置
- * @param endPosition 结束位置
- */
- public FileDownloadThread(URL url, File file, int startPosition,
- int endPosition) {
- this.url = url;
- this.file = file;
- this.startPosition = startPosition;
- this.curPosition = startPosition;
- this.endPosition = endPosition;
- Log.e(TAG, toString());
- }
- @Override
- public void run() {
- BufferedInputStream bis = null;
- RandomAccessFile rAccessFile = null;
- byte[] buf = new byte[BUFF_SIZE];
- URLConnection conn = null;
- try {
- conn = url.openConnection();
- conn.setConnectTimeout(10000);//设置超时
- conn.setReadTimeout(10000);
- conn.setAllowUserInteraction(true);
- System.out.println(this.getName()+" startPosition "+startPosition+" endPosition "+endPosition);
- conn.setRequestProperty("Range", "bytes="+(startPosition)+"-"+endPosition); //取剩余未下载的
- rAccessFile = new RandomAccessFile(file,"rwd");//读写
- //设置从什么位置开始写入数据
- rAccessFile.seek(startPosition);
- bis = new BufferedInputStream(conn.getInputStream(), BUFF_SIZE);
- while(curPosition<endPosition) //当前位置小于结束位置 继续下载
- {
- int len = bis.read(buf,0,BUFF_SIZE);
- if(len==-1) //下载完成
- {
- break;
- }
- rAccessFile.write(buf,0,len);
- curPosition = curPosition +len;
- if(curPosition > endPosition)
- { //如果下载多了,则减去多余部分
- System.out.println(" curPosition > endPosition !!!!");
- int extraLen = curPosition-endPosition;
- downloadSize += (len-extraLen+1);
- }else{
- downloadSize+=len;
- }
- }
- this.finished = true; //当前阶段下载完成
- Log.e(TAG, "当前"+this.getName()+"下载完成");
- } catch (Exception e) {
- Log.e(TAG, "download error Exception "+e.getMessage());
- e.printStackTrace();
- }finally{
- //关闭流
- FileUtil.closeInputStream(bis);
- try {
- rAccessFile.close();
- } catch (IOException e) {
- // TODO Auto-generated catch block
- Log.e("AccessFile", "AccessFile IOException "+e.getMessage());
- }
- }
- super.run();
- }
- /**
- * 是否完成当前段下载完成
- * @return
- */
- public boolean isFinished() {
- return finished;
- }
- /**
- * 已经下载多少
- * @return
- */
- public int getDownloadSize() {
- return downloadSize;
- }
- @Override
- public String toString() {
- return "FileDownloadThread [url=" + url + ", file=" + file
- + ", startPosition=" + startPosition + ", endPosition="
- + endPosition + ", curPosition=" + curPosition + ", finished="
- + finished + ", downloadSize=" + downloadSize + "]";
- }
多线程下载是分段下载,创建保存一个文件,子线程分别通过RandomAccessFile类进行写入操作。
转载:http://blog.csdn.net/chaoyu168/article/details/50971687