您的位置:首页 > 编程语言

这是我以前copy的关于多线程 4000 下载代码,来源我忘记了,我进行了注解。个人认知,不详细之处还请谅解

2016-01-29 13:36 537 查看
这是源码:
package teachercode;

import java.io.File;

import java.io.FileNotFoundException;

import java.io.IOException;

import java.io.InputStream;

import java.io.RandomAccessFile;

import java.net.HttpURLConnection;

import java.net.URL;

public class MultyThreadDownload {

 static class DownloadTask implements Runnable {

  private long start;

  private long end;

  private String path;

  private File destFile;

  public DownloadTask(long start, long end,String path, File destFile) {

   this.start = start;

   this.end = end;

   this.path = path;

   this.destFile = destFile;

  }

  @Override

  public void run() {

   RandomAccessFile dest = null;

   InputStream is = null;

   HttpURLConnection conn = null;

   try {

    // 4.线程读写

    URL url = new URL(path);

    conn = (HttpURLConnection) url.openConnection();

    conn.setRequestMethod("GET");

    is = conn.getInputStream();

    is.skip(start);// 设置源的开始位置

    dest = new RandomAccessFile(destFile, "rw");

    dest.seek(start);// 设置写开始的位置

    byte b[] = new byte[1000];//1.设置数组的大小  2.

    long total = start;//记录上一次下载的位置

    int len = 0;

    // start + len

    while ((len = is.read(b)) > 0) {

     total = total + len;

     if (total > end) {

      len = (int) (len - (total - end));

      dest.write(b, 0, len);

      break;

     } else {

      dest.write(b, 0, len);

     }

    }

    System.out.println(Thread.currentThread().getName()+"完成了下载,下载的数据范围是"+":["+start+","+end+")");

   } catch (FileNotFoundException e) {

    e.printStackTrace();

   } catch (IOException e) {

    e.printStackTrace();

   } finally {

    if(conn != null){

     conn.disconnect();

    }

    if (is != null) {

     try {

      is.close();

     } catch (IOException e) {

      e.printStackTrace();

     }

    }

    if (dest != null) {

     try {

      dest.close();

     } catch (IOException e) {

      e.printStackTrace();

     }

    }

   }

  }

 }

  public static void main(String[] args) {

   RandomAccessFile dest = null;

   try {

    // 1.获得服务器文件的大小,在本地新建一个相同大小的文件

    // 服务器文件

    String path = "http://n.sinaimg.cn/transform/20150806/4mXJ-fxftkps3371034.jpg";

    URL url = new URL(path);

    HttpURLConnection conn = (HttpURLConnection) url.openConnection();

    conn.setRequestMethod("GET");

//    InputStream is = conn.getInputStream();//关联服务器上的文件输入流

    // 在本地新建一个文件和服务器大小是一样

    String filestr = path.substring(path.lastIndexOf("/") + 1);

    File dir = new File("f:\\temp\\download");

    if(!dir.exists()){

     dir.mkdir();//创建目录

    }

    File destFile = new File(dir,filestr);//要写入的文件

    dest = new RandomAccessFile(destFile,"rw");

    long length =  conn.getContentLength();//服务器文件的长度

    dest.setLength(length);//设置本地文件的长度

    // 2.决定要开几个线程下载

    int num = 3;

    long block =length / num;// length = 10 block=3

    long start, end;

    // 3.计算每个线程开始和结束的位置,开线程下载

    for (int i = 0; i < num; i++) {

     start = block * i;

     if (i == num - 1) {

      end = length;

     } else {

      end = start + block;

     }

     // 开线程并启动

     new Thread(new DownloadTask(start, end, path, destFile)).start();

    }

   } catch (IOException e) {

    e.printStackTrace();

   } finally {

    if (dest != null) {

     try {

      dest.close();

     } catch (IOException e) {

      e.printStackTrace();

     }

    }

   }

  }

}

具体注解:

1. 首先获取服务器文件的大小,在本地新建一个相同大小的文件。

String path = "http://n.sinaimg.cn/transform/20150806/4mXJ-fxftkps3371034.jpg";

URL url = new URL(path);

获得所要下载的文件路径path,将path传到URL实例url中,

HttpURLConnection conn = (HttpURLConnection) url.openConnection();

onn.setRequestMethod("GET");

然后获取url.openConnection   赋给HttpURLConnection实例conn中

通过conn.setRequestMethod(“GET”)请求获取静态页面

String filestr = path.substring(path.lastIndexOf("/") + 1);

创建一个filestr来获得path地址中最后一个”/”之后的数据;

File dir = new File("f:\\temp\\download");

if(!dir.exists()){

   dir.mkdir();//创建目录

}

File destFile = new File(dir,filestr);//要写入的文件

dest = new RandomAccessFile(destFile,"rw");

通过File 创建目录,建立文件destFile,使用RandomAccessFile对创建的文件(destFile)进行读写操作,赋给dest。

long length = conn.getContentLength();

dest.setLength(length);

通过conn的getContentLength()方法获取服务器文件长度。再通过dest中的setLength(getContentLength())方法对本地文件设置长度。

2. 多线程下载分块操作

int num = 3;

long block =length/num;

定义线程数目num,使用文件长度length对num整除获取分区块数。

long start, end;

for (int i = 0; i < num; i++) {

   start = block * i;

   if (i == num - 1) {

    end = length;

   } else {

    end = start + block;

     }

  new Thread(new DownloadTask(start, end, path, destFile)).start();

}

计算每个线程的开始(start)和结束(end)位置,用for循环依次对线程分配下载的开始和结束位置,开始为:块数大小(block)*序号(i)。

第一次开始位置:0        第一次结束位置:end=0+block;

第二次开始位置:block     第二次结束位置:end=block+block;

第三次开始位置:2*block   第三次结束位置:end=length;

由于是采用”/”整除方式,最后一块区域可能放不下文件数据。

所以当下载最后块数(第三块区域)时候,有可能所下载的长度大于 块数长度,这时候要将end为length。

 

3. 线程启动,线程开始对文件读写

线程启动代码:new Thread(new DownloadTask(start, end, path, destFile)).start();

通过for循环 对每个线程开启,并将start,end,path,File destFile,传入DownloadTask中(实现Runnable接口),重写Run方法:在run里完成对文件读写。

URL url = new URL(path);

HttpURLConnection  conn = (HttpURLConnection) url.openConnection();

conn.setRequestMethod("GET");

获得所要下载的文件路径path,将path传到URL实例url中,

然后获取url.openConnection   赋给HttpURLConnection实例conn中

通过conn.setRequestMethod(“GET”)请求获取静态页面

InputStream is = conn.getInputStream();

is.skip(start);

创建输入流is,关联coon的文件输入流。用输入流is方法中的skip(start),设置源的开始位置。

dest = new RandomAccessFile(destFile, "rw");

dest.seek(start);

通过RandomAccessFile对目的文件destFile读写操作:使用seek(start)定位到写 开始位置。然后开始写入数据。

byte b[] = new byte[1000];

long total = start;

int len = 0;

while ((len = is.read(b)) > 0) {

     total = total + len;

     if (total > end) {

      len = (int) (len - (total - end));

      dest.write(b, 0, len);

      break;

     } else {

      dest.write(b, 0, len);

     }

    }

设置读取数组的大小b[]=byte[1000];

While()循环写入数据,开始位置为start,结束位置为end,每读取一次(1000)记录一下上次下载的位置(total ),方法:total = total+len,len为读取长度,初始化为0,write(b,0,len);当total>end(即:说明最后读取长度超过 本块区域(因为文件平分三块)),最后一点区域写入方法:len = len-(total - end)然后write(b,0,len).写入数据。

System.out.println(Thread.currentThread().getName()+"完成了下载,下载的数据范围是"+":["+start+","+end+")");

这段代码测试作用:最后为了方便查看哪个线程下载了哪段区域,使用Thread.currentThread().getName()获得当前线程名字[start,end)  获取区域。


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  多线程 下载