zoukankan      html  css  js  c++  java
  • Java_java多线程下载-断点下载-超详细

    基本原理:利用URLConnection获取要下载文件的长度、头部等相关信息,并设置响应的头部信息。并且通过URLConnection获取输入流,将文件分成指定的块,每一块单独开辟一个线程完成数据的读取、写入。通过输入流读取下载文件的信息,然后将读取的信息用RandomAccessFile随机写入到本地文件中。同时,每个线程写入的数据都文件指针也就是写入数据的长度,需要保存在一个临时文件中。这样当本次下载没有完成的时候,下次下载的时候就从这个文件中读取上一次下载的文件长度,然后继续接着上一次的位置开始下载。并且将本次下载的长度写入到这个文件中。




    import java.io.File;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.RandomAccessFile;
    import java.net.HttpURLConnection;
    import java.net.URL;
    import java.text.SimpleDateFormat;
    import java.util.Date;


    /**
     * 多线程下载
     * 2013-07-17 20:34:32
     *
     */
    public class DownHelper {
    static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("HH:mm:ss");


    public static void main(String[] args) throws Exception {
    int threadCount = 50; // 线程总数
    //下载文件地址
    String path = "http://dldir1.qq.com/qqfile/qq/QQ2013/2013Beta6/7354/QQ2013Beta6.exe"; 
    URL url = new URL(path);
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    conn.setRequestMethod("GET");
    if (conn.getResponseCode() == 200) { // 打开连接
    int length = conn.getContentLength(); // 获得文件大小
    // 计算每个线程 分配的下载数据量大小
    int block = length % threadCount == 0 ? length / threadCount : length / threadCount + 1;
    //穿创建一个保存数据的文件
    File file = new File(getFileName(path));
    //文件访问类   rwd 模式,可写入、可读取,写入后立刻推送到硬盘
    RandomAccessFile accessFile = new RandomAccessFile(file, "rwd");
    //给文件设定大小
    accessFile.setLength(length);
    accessFile.close();
    // 输出文件信息
    System.out.println(DATE_FORMAT.format(new Date()) + " 开始下载文件 : " + path + " 文件总大小 : " + (length / 1024D / 1024D) + " mb");
    System.out.println("文件保存路径:" + file.getAbsolutePath());
    for (int i = 0; i < threadCount; i++) {
    //启动N个线程,为每个线程分配自己的 数据 块
    new DoubleDownThread(i, url, file, block).start();
    }
    }


    }


    private static String getFileName(String path) {
    return "C:/" + path.substring(path.lastIndexOf("/") + 1);
    }
    }


    class DoubleDownThread extends Thread {
    private int id;//线程ID
    private URL url;//下载文件的连接
    private File file;//保存到文件路径
    private int block;//数据块大小


    public DoubleDownThread(int id, URL url, File file, int block) {
    super();
    this.id = id;
    this.url = url;
    this.file = file;
    this.block = block;
    }


    @Override
    public void run() {
    try {
    ////文件访问类   rwd 模式,可写入、可读取,写入后立刻推送到硬盘
    RandomAccessFile accessFile = new RandomAccessFile(file, "rwd");
    /**
    * 比如一个长度为 10字节的数据,3个线程下载
    * 根据 算法分配
    * if(数据长度 % 线程数 == 0){
    * 分配块 = 数据长度/线程数
    * } else {
    * 分配块 = 数据长度/线程数 + 1
    * }
    * 数据长度为 10 线程为 3 。
    * 则每个线程分得块大小为 4
    * 第一个线程起止位置是 0 - 3
    * 第二个线程起止位置是 4 - 7
    * 第三个线程起止位置是 8 - 11

    * 因为数据长度总共才 10 字节。所以读取的时候。发现到了10 就读取不到数据,程序自然不会继续读取不存在的 第11个字节
    */
    //计算文件写入起始位置
    int start = id * block;
    //计算文件写入结束位置
    int end = (id + 1) * block - 1;
    //打开文件下载链接
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    //设置连接超时异常等待时间
    conn.setConnectTimeout(5000);

    conn.setRequestMethod("GET");
    //断点下载,不需要请求整个文件,为请求设置请求头。Range 表示局部请求下载文件  bytes=起始位置-结束位置
    conn.setRequestProperty("Range", "bytes=" + start + "-" + end);
    //局部请求。成功的标志不再是 200, 而是 206
    if (conn.getResponseCode() == 206) {
    //将本地文件的指针移动到要写入的位置
    accessFile.seek(start);
    //获得网络文件输入流,开始写入文件
    InputStream in = conn.getInputStream();
    byte[] bys = new byte[1024];
    int length = 0;
    while ((length = in.read(bys, 0, bys.length)) != -1) {
    accessFile.write(bys, 0, length);
    }
    //下载完成。,关闭流
    accessFile.close();
    in.close();
    }
    //输出线程下载文件的 提示
    System.out.println(DownHelper.DATE_FORMAT.format(new Date()) + " 线程" + (id + 1) + ":下载完成 ");
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }




    使用JAVA实现HTTP多线程下载

           下载工具我想没有几个人不会用的吧,前段时间比较无聊,花了点时间用java写了个简单的http多线程下载程序,纯粹是无聊才写的,只实现了几个简单的功能,而且也没写界面,今天正好也是一个无聊日,就拿来写篇文章,班门弄斧一下,觉得好给个掌声,不好也不要喷,谢谢!

    我实现的这个http下载工具功能很简单,就是一个多线程以及一个断点恢复,当然下载是必不可少的。那么大概先整理一下要做的事情:

    1、 连接资源服务器,获取资源信息,创建文件

    2、 切分资源,多线程下载

    3、 断点恢复功能

    4、 下载速率统计

    大概就这几点吧,那么首先要做的就是连接资源并获取资源信息,我这里使用了JavaSE自带的URLConnection进行资源连接,大致代码如下:

     1                      String urlStr  =  “http: // www.sourcelink.com/download/xxx”;    // 资源地址,随便写的
     2
     3             URL url  =   new  URL(urlStr);                              // 创建URL
     4
     5             URLConnection con  =  url.openConnection();                // 建立连接
     6
     7             contentLen  =  con.getContentLength();                     // 获得资源长度
     8
     9 File file  =   new  File(filename);                                             // 根据filename创建一个下载文件,也会是我们最终下载所得的文件
    10

    很简单吧,没错就是这么简单,第一步做完了,那么接下来要做第二步,切分资源,实现多线程。在上一步我们已经获得了资源的长度contentLen,那么如何根据这个对资源进行切分呢?假如我们要运行十个线程,那么我们就先把contentLen处以10,获得每块的大小,然后在分别创建十个线程,每个线程负责其中一块的写入,这就需要利用到RandomAccessFile这个类了,这个类提供了对文件的随机访问,可以指定向文件中的某一个位置进行写入操作,大致代码如下:

                 long  subLen  =  contentLen  /  threadQut;                            // 获取每块的大小

                
    // 创建十个线程,并启动线程
                 for  ( int  i  =   0 ; i  <  threadQut; i ++ {
                    DLThread thread 
    = new DLThread(this, i + 1, subLen * i, subLen * (i + 1- 1); //创建线程
                    dlThreads[i] = thread;
                    QSEngine.pool.execute(dlThreads[i]);                                
    //把线程交给线程池进行管理
                }


     

    在这里使用到了DLThread这个类,我们先来看看这个类的构造方法的定义:

    public DLThread(DLTask dlTask, int id, long startPos, long endPos)

    第一个参数为一个DLTask,这个类就代表一个下载任务,里面主要保存这一个下载任务的信息,包括下载资源名,本地文件名等等的信息。第二个参数就是一个标示线程的id,如果有10个线程,那么这个id就是从1到10,第三个参数startPos代表该线程从文件的哪个地方开始写入,最后一个参数endPos代表写到哪里就结束。

    我们再来看看,一个线程启动后,具体如何去下载,请看run方法:

         public   void  run()  {
            System.out.println(
    "线程" + id + "启动");
            BufferedInputStream bis 
    = null;                                             //创建一个buff
            RandomAccessFile fos = null;                                               
            
    byte[] buf = new byte[BUFFER_SIZE];                                         //缓冲区大小
            URLConnection con = null;
            
    try {
                con 
    = url.openConnection();                                             //创建连接,这里会为每个线程都创建一个连接
                con.setAllowUserInteraction(true);
                
    if (isNewThread) {
                    con.setRequestProperty(
    "Range""bytes=" + startPos + "-" + endPos);//设置获取资源数据的范围,从startPos到endPos
                    fos = new RandomAccessFile(file, "rw");                             //创建RandomAccessFile
                    fos.seek(startPos);                                                 //从startPos开始
                }
     else {
                    con.setRequestProperty(
    "Range""bytes=" + curPos + "-" + endPos);
                    fos 
    = new RandomAccessFile(dlTask.getFile(), "rw");
                    fos.seek(curPos);
                }

                
    //下面一段向根据文件写入数据,curPos为当前写入的未知,这里会判断是否小于endPos,
                
    //如果超过endPos就代表该线程已经执行完毕
                bis = new BufferedInputStream(con.getInputStream());                    
                
    while (curPos < endPos) {
                    
    int len = bis.read(buf, 0, BUFFER_SIZE);                
                    
    if (len == -1{
                        
    break;
                    }

                    fos.write(buf, 
    0, len);
                    curPos 
    = curPos + len;
                    
    if (curPos > endPos) {
                        readByte 
    += len - (curPos - endPos) + 1//获取正确读取的字节数
                    }
     else {
                        readByte 
    += len;
                    }

                }

                System.out.println(
    "线程" + id + "已经下载完毕。");
                
    this.finished = true;
                bis.close();
                fos.close();
            }
     catch (IOException ex) {
                ex.printStackTrace();
                
    throw new RuntimeException(ex);
            }

        }


     

    上面的代码就是根据startPos和endPos对文件机型写操作,每个线程都有自己独立的一个资源块,从startPos到endPos。上面的方式就是线程下载的核心,多线程搞定后,接下来就是实现断点恢复的功能,其实断点恢复无非就是记录下每个线程完成到哪个未知,在这里我就是使用curPos进行的记录,大家在上面的代码就应该可以看到,我会记录下每个线程的curPos,然后在线程重新启动的时候,就把curPos当成是startPos,而endPost则不变即可,大家有没注意到run方法里有一段这样的代码:

                 if  (isNewThread)  {                                              //判断是否断点,如果true,代表是一个新的下载线程,而不是断点恢复
                    con.setRequestProperty("Range""bytes=" + startPos + "-" + endPos);//设置获取资源数据的范围,从startPos到endPos
                    fos = new RandomAccessFile(file, "rw");                             //创建RandomAccessFile
                    fos.seek(startPos);                                                 //从startPos开始
                }
      else   {
                    con.setRequestProperty(
    "Range""bytes=" + curPos + "-" + endPos);//使用curPos替代startPos,其他都和新创建一个是一样的。
                    fos = new RandomAccessFile(dlTask.getFile(), "rw");
                    fos.seek(curPos);
                }


     

    上面就是断点恢复的做法了,和新创建一个线程没什么不同,只是startPos不一样罢了,其他都一样,不过仅仅有这个还不够,因为如果程序关闭的话,这些信息又是如何保存呢?例如文件名啊,每个线程的curPos啊等等,大家在使用下载软件的时候,相信都会发现在软件没下载完的时候,在目录下会有两个临时文件,而其中一个就是用来保存下载任务的信息的,如果没有这些信息,程序是不知道该如何恢复下载进度的。而我这里又如何实现的呢?我这个人比较懒,又不想再创建一个文件来保存信息,然后自己又要读取信息创建对象,那太麻烦了,所以我想到了java提供序列化机制,我的想法就是直接把整个DLTask的对象序列化到硬盘上,上面说过DLTask这个类就是用来保存每个任务的信息的,所以我只要在需要恢复的时候,反序列化这个对象,就可以很容易的实现了断点功能,我们来看看这个对象保存的信息:

    public   class  DLTask  extends  Thread  implements  Serializable  {

        
    private static final long serialVersionUID = 126148287461276024L;
        
    private final static int MAX_DLTHREAD_QUT = 10;  //最大下载线程数量
        /**
         * 下载临时文件后缀,下载完成后将自动被删除
         
    */

        
    public final static String FILE_POSTFIX = ".tmp";
        
    private URL url;                                    
        
    private File file;
        
    private String filename;
        
    private int id;
        
    private int Level;
        
    private int threadQut;                                //下载线程数量,用户可定制                            
        private int contentLen;                            //下载文件长度
        private long completedTot;                            //当前下载完成总数
        private int costTime;                                //下载时间计数,记录下载耗费的时间
        private String curPercent;                            //下载百分比
        private boolean isNewTask;                        //是否新建下载任务,可能是断点续传任务
        
        
    private DLThread[] dlThreads;                        //保存当前任务的线程

    transient private DLListener listener;            //当前任务的监听器,用于即时获取相关下载信息

     

    如上代码,这个对象实现了Serializable接口,保存了任务的所有信息,还包括有每个线程对象dlThreads,这样子就可以很容易做到断点的恢复了,让我重新写一个文件保存这些信息,然后在恢复的时候再根据这些信息创建一个对象,那简直是要我的命。这里创建了一个方法,用于断点恢复用:

         private   void  resumeTask()  {
            listener 
    = new DLListener(this);
            file 
    = new File(filename);
            
    for (int i = 0; i < threadQut; i++{
                dlThreads[i].setDlTask(
    this);
                QSEngine.pool.execute(dlThreads[i]);
            }

            QSEngine.pool.execute(listener);
        }



     

    实际上就是减少了先连接资源,然后进行切分资源的代码,因为这些信息已经都被保存在DLTask的对象下了。

    看到上面的代码,不知道大家注意到有一个对象DLListener没有,这个对象实际上就是用于监听整个任务的信息的,这里我主要用于两个目的,一个是定时的对DLTask进行序列化,保存任务信息,用于断点恢复,一个就是进行下载速率的统计,平均多长时间进行一个统计。我们先来看下它的代码,这个类也是一个单独的线程:

         public   void  run()  {

            
    int i = 0;
            BigDecimal completeTot 
    = null;                                         //完成的百分比             
            long start = System.currentTimeMillis();                               //当前时间,用于记录开始统计时间
            long end = start;

            
    while (!dlTask.isComplete()) {                                        //整个任务是否完成,没有完成则继续循环
                i++;
                String percent 
    = dlTask.getCurPercent();                      //获取当前的完成百分数

                completeTot 
    = new BigDecimal(dlTask.getCompletedTot());       //获取当前完成的总字节数

                            
    //获得当前时间,然后与start时间比较,如果不一样,利用当前完成的总数除以所使用的时间,获得一个平均下载速度
                end = System.currentTimeMillis();                             
                
    if (end - start != 0{
                    BigDecimal pos 
    = new BigDecimal(((end - start) / 1000* 1024);
                    System.out.println(
    "Speed :"
                            
    + completeTot
                                    .divide(pos, 
    0, BigDecimal.ROUND_HALF_EVEN)
                            
    + "k/s   " + percent + "% completed. ");
                }

                recoder.record();         
    //将任务信息记录到硬盘
                try {
                    sleep(
    3000);
                }
     catch (InterruptedException ex) {
                    ex.printStackTrace();
                    
    throw new RuntimeException(ex);
                }


            }

                    
    //以下是下载完成后打印整个下载任务的信息
            int costTime =+ (int)((System.currentTimeMillis() - start) / 1000);
            dlTask.setCostTime(costTime);
            String time 
    = QSDownUtils.changeSecToHMS(costTime);
            
            dlTask.getFile().renameTo(
    new File(dlTask.getFilename()));
            System.out.println(
    "Download finished. " + time);
        }


     

    这个方法中的recoder.record()方法的调用就是用于序列化任务对象,其他的代码均为统计信息用的,具体可看注释,record该方法的代码如下:

         public   void  record()  {
            ObjectOutputStream out 
    = null;
            
    try {
                out 
    = new ObjectOutputStream(new FileOutputStream(dlTask.getFilename() + ".tsk"));  
                out.writeObject(dlTask);
                out.close();
            }
     catch (IOException ex) {
                ex.printStackTrace();
                
    throw new RuntimeException(ex);
            }
     finally {
                
    try {
                    out.close();
                }
     catch (IOException ex) {
                    ex.printStackTrace();
                    
    throw new RuntimeException(ex);
                }

            }


        }



    (一)断点续传的原理

    其实断点续传的原理很简单,就是在Http的请求上和一般的下载有所不同而已。打个比方,浏览器请求服务器上的一个文时,所发出的请求如下:

    假设服务器域名为wwww.sjtu.edu.cn,文件名为down.zip。

    GET /down.zip HTTP/1.1
    Accept: image/gif, image/x-xbitmap, image/jpeg,image/pjpeg, application/vnd.ms-
    excel, application/msword,application/vnd.ms-powerpoint, */*
    Accept-Language: zh-cn
    Accept-Encoding: gzip, deflate
    User-Agent: Mozilla/4.0 (compatible; MSIE 5.01; WindowsNT 5.0)
    Connection: Keep-Alive
    服务器收到请求后,按要求寻找请求的文件,提取文件的信息,然后返回给浏览器,返回信息如下:

    200
    Content-Length=106786028
    Accept-Ranges=bytes
    Date=Mon, 30 Apr 2001 12:56:11 GMT
    ETag=W/"02ca57e173c11:95b"
    Content-Type=application/octet-stream
    Server=Microsoft-IIS/5.0
    Last-Modified=Mon, 30 Apr 2001 12:56:11 GMT

    所谓断点续传,也就是要从文件已经下载的地方开始继续下载。所以在客户端浏览器传给
    Web服务器的时候要多加一条信息--从哪里开始。

    下面是用自己编的一个"浏览器"来传递请求信息给Web服务器,要求从2000070字节开始。

    GET /down.zip HTTP/1.0
    User-Agent: NetFox
    RANGE: bytes=2000070-
    Accept: text/html, image/gif, image/jpeg, *; q=.2, */*;q=.2

    仔细看一下就会发现多了一行RANGE: bytes=2000070-;这一行的意思就是告诉服务器down.zip这个文件从2000070字节开始传,前面的字节不用传了。

    服务器收到这个请求以后,返回的信息如下:

    206
    Content-Length=106786028
    Content-Range=bytes 2000070-106786027/106786028
    Date=Mon, 30 Apr 2001 12:55:20 GMT
    ETag=W/"02ca57e173c11:95b"
    Content-Type=application/octet-stream
    Server=Microsoft-IIS/5.0
    Last-Modified=Mon, 30 Apr 2001 12:55:20 GMT

    和前面服务器返回的信息比较一下,就会发现增加了一行:

    Content-Range=bytes 2000070-106786027/106786028

    返回的代码也改为206了,而不再是200了。

    知道了以上原理,就可以进行断点续传的编程了。
    (二)Java实现断点续传的关键几点

    (1)用什么方法实现提交RANGE:bytes=2000070-。
    当然用最原始的Socket是肯定能完成的,不过那样太费事了,其实Java的net包中提供了这种功能。代码如下:

    URL url = new URL(" ;" TARGET=_blankclass=ilink>http://www.sjtu.edu.cn/down.zip";;
    HttpURLConnection httpConnection =(HttpURLConnection)url.openConnection

    ();
    //设置User-Agent
    httpConnection.setRequestProperty("User-Agent","NetFox");
    //设置断点续传的开始位置
    httpConnection.setRequestProperty("RANGE","bytes=2000070");
    //获得输入流
    InputStream input = httpConnection.getInputStream();

    从输入流中取出的字节流就是down.zip文件从2000070开始的字节流。大家看,其实断点续传用Java实现起来还是很简单的吧。接下来要做的事就是怎么保存获得的流到文件中去了。

    保存文件采用的方法

    我采用的是IO包中的RandAccessFile类。

    操作相当简单,假设从2000070处开始保存文件,代码如下:

    RandomAccess oSavedFile = newRandomAccessFile("down.zip","rw");
    long nPos = 2000070;
    //定位文件指针到nPos位置
    oSavedFile.seek(nPos);
    byte[] b = new byte[1024];
    int nRead;
    //从输入流中读入字节流,然后写到文件中
    while((nRead=input.read(b,0,1024)) > 0)
    {
    oSavedFile.write(b,0,nRead);
    }

    接下来要做的就是整合成一个完整的程序了。包括一系列的线程控制等等。

    (三)断点续传内核的实现

    主要用了6个类,包括一个测试类:

    SiteFileFetch.java负责整个文件的抓取,控制内部线程(FileSplitterFetch类)。

    FileSplitterFetch.java负责部分文件的抓取。

    FileAccess.java负责文件的存储。

    SiteInfoBean.java要抓取的文件的信息,如文件保存的目录,名字,抓取文件的URL等。

    Utility.java工具类,放一些简单的方法。

    TestMethod.java测试类。

    下面是源程序:

    /*
    **SiteFileFetch.java
    */
    package NetFox;
    import java.io.*;
    import java.net.*;

    public class SiteFileFetch extends Thread {

    SiteInfoBean siteInfoBean = null; //文件信息Bean
    long[] nStartPos; //开始位置
    long[] nEndPos; //结束位置
    FileSplitterFetch[] fileSplitterFetch; //子线程对象
    long nFileLength; //文件长度
    boolean bFirst = true; //是否第一次取文件
    boolean bStop = false; //停止标志
    File tmpFile; //文件下载的临时信息
    DataOutputStream output; //输出到文件的输出流

    public SiteFileFetch(SiteInfoBean bean) throwsIOException
    {
    siteInfoBean= bean;
    //tmpFile= File.createTempFile ("zhong","1111",newFile(bean.getSFilePath()));
    tmpFile= new File(bean.getSFilePath()+File.separator +bean.getSFileName()+".info");
    if(tmpFile.exists())
    {
    bFirst= false;
    read_nPos();
    }
    else
    {
    nStartPos= new long[bean.getNSplitter()];
    nEndPos= new long[bean.getNSplitter()];
    }
    }

    public void run()
    {
    //获得文件长度
    //分割文件
    //实例FileSplitterFetch
    //启动FileSplitterFetch线程
    //等待子线程返回
    try{
    if(bFirst)
    {
    nFileLength= getFileSize();
    if(nFileLength== -1)
    {
    System.err.println("FileLength is not known!");
    }
    elseif(nFileLength == -2)
    {
    System.err.println("Fileis not access!");
    }
    else
    {
    for(inti=0;i<nStartPos.length;i++)
    {
    nStartPos[i]= (long)(i*(nFileLength/nStartPos.length));
    }
    for(inti=0;i<nEndPos.length-1;i++)
    {
    nEndPos[i]= nStartPos[i+1];
    }
    nEndPos[nEndPos.length-1]= nFileLength;
    }
    }

    //启动子线程
    fileSplitterFetch= new FileSplitterFetch[nStartPos.length];
    for(inti=0;i<nStartPos.length;i++)
    {
    fileSplitterFetch[i]= new FileSplitterFetch(siteInfoBean.getSSiteURL(),
    siteInfoBean.getSFilePath()+ File.separator + siteInfoBean.getSFileName(), nStartPos[i],nEndPos[i],i);
    Utility.log("Thread" + i + " , nStartPos = " + nStartPos[i] + ", nEndPos =" + nEndPos[i]);
    fileSplitterFetch[i].start();
    }
    //fileSplitterFetch[nPos.length-1] = newFileSplitterFetch(siteInfoBean.getSSiteURL(),
    siteInfoBean.getSFilePath()+ File.separator + siteInfoBean.getSFileName(),nPos[nPos.length-1],nFileLength,nPos.length-1);
    //Utility.log("Thread " + (nPos.length-1) + " , nStartPos = "+ nPos[nPos.length-1] + ", nEndPos = " + nFileLength);
    //fileSplitterFetch[nPos.length-1].start();

    //等待子线程结束
    //intcount = 0;
    //是否结束while循环
    booleanbreakWhile = false;

    while(!bStop)
    {
    write_nPos();
    Utility.sleep(500);
    breakWhile= true;

    for(inti=0;i<nStartPos.length;i++)
    {
    if(!fileSplitterFetch[i].bDownOver)
    {
    breakWhile= false;
    break;
    }
    }
    if(breakWhile)
    break;

    //count++;
    //if(count>4)
    //siteStop();
    }

    System.err.println("文件下载结束!");
    }
    catch(Exception e){e.printStackTrace ();}
    }

    //获得文件长度
    public long getFileSize()
    {
    intnFileLength = -1;
    try{
    URLurl = new URL(siteInfoBean.getSSiteURL());
    HttpURLConnectionhttpConnection = (HttpURLConnection)url.openConnection ();
    httpConnection.setRequestProperty("User-Agent","NetFox");

    intresponseCode=httpConnection.getResponseCode();
    if(responseCode>=400)
    {
    processErrorCode(responseCode);
    return-2; //-2 represent access is error
    }

    StringsHeader;

    for(inti=1;;i++)
    {
    //DataInputStreamin = new DataInputStream(httpConnection.getInputStream ());
    //Utility.log(in.readLine());
    sHeader=httpConnection.getHeaderFieldKey;
    if(sHeader!=null)
    {
    if(sHeader.equals("Content-Length"))
    {
    nFileLength= Integer.parseInt(httpConnection.getHeaderField(sHeader));
    break;
    }
    }
    else
    break;
    }
    }
    catch(IOExceptione){e.printStackTrace ();}
    catch(Exceptione){e.printStackTrace ();}

    Utility.log(nFileLength);
    returnnFileLength;
    }

    //保存下载信息(文件指针位置)
    private void write_nPos()
    {
    try{
    output= new DataOutputStream(new FileOutputStream(tmpFile));
    output.writeInt(nStartPos.length);
    for(inti=0;i<nStartPos.length;i++)
    {
    //output.writeLong(nPos[i]);
    output.writeLong(fileSplitterFetch[i].nStartPos);
    output.writeLong(fileSplitterFetch[i].nEndPos);
    }
    output.close();
    }
    catch(IOExceptione){e.printStackTrace ();}
    catch(Exceptione){e.printStackTrace ();}
    }

    //读取保存的下载信息(文件指针位置)
    private void read_nPos()
    {
    try{
    DataInputStreaminput = new DataInputStream(new FileInputStream(tmpFile));
    intnCount = input.readInt();
    nStartPos= new long[nCount];
    nEndPos= new long[nCount];
    for(inti=0;i<nStartPos.length;i++)
    {
    nStartPos[i]= input.readLong();
    nEndPos[i]= input.readLong();
    }
    input.close();
    }
    catch(IOExceptione){e.printStackTrace ();}
    catch(Exceptione){e.printStackTrace ();}
    }

    private void processErrorCode(int nErrorCode)
    {
    System.err.println("ErrorCode : " + nErrorCode);
    }

    //停止文件下载
    public void siteStop()
    {
    bStop= true;
    for(inti=0;i<nStartPos.length;i++)
    fileSplitterFetch[i].splitterStop();

    }
    }
    /*
    **FileSplitterFetch.java
    */
    package NetFox;

    import java.io.*;
    import java.net.*;

    public class FileSplitterFetch extends Thread {

    StringsURL; //File URL
    longnStartPos; //File Snippet Start Position
    longnEndPos; //File Snippet End Position
    intnThreadID; //Thread's ID
    booleanbDownOver = false; //Downing is over
    booleanbStop = false; //Stop identical
    FileAccessIfileAccessI = null; //File Access interface

    publicFileSplitterFetch(String sURL,String sName,long nStart,long nEnd,int id) throwsIOException
    {
    this.sURL= sURL;
    this.nStartPos= nStart;
    this.nEndPos= nEnd;
    nThreadID= id;
    fileAccessI= new FileAccessI(sName,nStartPos);
    }

    public void run()
    {
    while(nStartPos< nEndPos && !bStop)
    {
    try{
    URLurl = new URL(sURL);
    HttpURLConnectionhttpConnection = (HttpURLConnection)url.openConnection ();
    httpConnection.setRequestProperty("User-Agent","NetFox");
    StringsProperty = "bytes="+nStartPos+"-";
    httpConnection.setRequestProperty("RANGE",sProperty);
    Utility.log(sProperty);
    InputStreaminput = httpConnection.getInputStream();
    //logResponseHead(httpConnection);

    byte[]b = new byte[1024];
    intnRead;
    while((nRead=input.read(b,0,1024))> 0 && nStartPos < nEndPos && !bStop)
    {
    nStartPos+= fileAccessI.write(b,0,nRead);
    //if(nThreadID== 1)
    //Utility.log("nStartPos = " + nStartPos + ", nEndPos = " +nEndPos);
    }

    Utility.log("Thread" + nThreadID + " is over!");
    bDownOver= true;
    //nPos= fileAccessI.write (b,0,nRead);
    }
    catch(Exceptione){e.printStackTrace ();}
    }
    }

    //打印回应的头信息
    public void logResponseHead(HttpURLConnection con)
    {
    for(inti=1;;i++)
    {
    Stringheader=con.getHeaderFieldKey;
    if(header!=null)
    //responseHeaders.put(header,httpConnection.getHeaderField(header));
    Utility.log(header+": "+con.getHeaderField(header));
    else
    break;
    }
    }

    public void splitterStop()
    {
    bStop= true;
    }
    }

    /*
    **FileAccess.java
    */
    package NetFox;
    import java.io.*;

    public class FileAccessI implements Serializable{

    RandomAccessFileoSavedFile;
    longnPos;

    publicFileAccessI() throws IOException
    {
    this("",0);
    }

    publicFileAccessI(String sName,long nPos) throws IOException
    {
    oSavedFile= new RandomAccessFile(sName,"rw");
    this.nPos= nPos;
    oSavedFile.seek(nPos);
    }

    publicsynchronized int write(byte[] b,int nStart,int nLen)
    {
    intn = -1;
    try{
    oSavedFile.write(b,nStart,nLen);
    n= nLen;
    }
    catch(IOExceptione)
    {
    e.printStackTrace();
    }

    returnn;
    }
    }

    /*
    **SiteInfoBean.java
    */
    package NetFox;

    public class SiteInfoBean {
    privateString sSiteURL; //Site's URL
    privateString sFilePath; //Saved File's Path
    privateString sFileName; //Saved File's Name
    privateint nSplitter; //Count of Splited Downloading File

    publicSiteInfoBean()
    {
    //defaultvalue of nSplitter is 5
    this("","","",5);
    }

    publicSiteInfoBean(String sURL,String sPath,String sName,int nSpiltter)
    {
    sSiteURL=sURL;
    sFilePath= sPath;
    sFileName= sName;
    this.nSplitter= nSpiltter;
    }

    publicString getSSiteURL()
    {
    returnsSiteURL;
    }

    publicvoid setSSiteURL(String value)
    {
    sSiteURL= value;
    }

    publicString getSFilePath()
    {
    returnsFilePath;
    }

    publicvoid setSFilePath(String value)
    {
    sFilePath= value;
    }

    publicString getSFileName()
    {
    returnsFileName;
    }

    publicvoid setSFileName(String value)
    {
    sFileName= value;
    }

    publicint getNSplitter()
    {
    returnnSplitter;
    }

    publicvoid setNSplitter(int nCount)
    {
    nSplitter= nCount;
    }
    }

    /*
    **Utility.java
    */
    package NetFox;

    public class Utility {

    publicUtility()
    {
    }

    publicstatic void sleep(int nSecond)
    {
    try{
    Thread.sleep(nSecond);
    }
    catch(Exceptione)
    {
    e.printStackTrace();
    }
    }

    public static void log(String sMsg)
    {
    System.err.println(sMsg);
    }

    public static void log(int sMsg)
    {
    System.err.println(sMsg);
    }
    }

    /*
    **TestMethod.java
    */
    package NetFox;

    public class TestMethod {

    publicTestMethod()
    {///xx/weblogic60b2_win.exe
    try{
    SiteInfoBeanbean = new SiteInfoBean(" http://localhost/xx/weblogic60b2_win.exe";;,"L: emp","weblogic60b2_win.exe",5);
    //SiteInfoBeanbean = new SiteInfoBean(" http://localhost:8080/down.zip";;,"L: emp","weblogic60b2_win.exe",5);
    SiteFileFetchfileFetch = new SiteFileFetch(bean);
    fileFetch.start();
    }
    catch(Exceptione){e.printStackTrace ();}
    }

    publicstatic void main(String[] args)
    {
    newTestMethod();
    }
    }


  • 相关阅读:
    个人总结
    第十六周学习进度条
    第二阶段冲刺第十天
    第二阶段冲刺第九天
    分层体系架构模式
    安卓语音识别
    AndroidStudio中导入jar包的方法
    Android中控件之间添加分割线
    按照分层设计理念,完成《XXX需求征集系统》的概念结构设计
    在Eclipse下搭建Hibernate框架(加载hibernate工具插件,离线)
  • 原文地址:https://www.cnblogs.com/javawebsoa/p/3206374.html
Copyright © 2011-2022 走看看