仿迅雷下载项目分析

一,下载项目分析:

1,单线程下载:
    网络  流

2,多线程下载
    a,每个线程下载多少数据。  文件总大小%线程数==0? 文件总大小%线程数:文件总大小/线程数+1;
    b,某个请从资源 的某个位置下载  => http协议请求头 Range: byte=xxx-xx
    c,在下载前,取出要下载的文件的大小,并在本地建立一个同大小的文件。
        RanddomAccessFile
            setFileSize();
    d,如何让线程找到文件的指定位置写入
        RanddomAccessFile
            seek(int position)
3,断点续传
    a,将没下完的文件记录到   属性文件/XML文件

4,迅雷最新的技术;
    多服务器下载,从不同的服务器找到文件源进行下载。 vip客户


这个项目主要是实现了第2个方案:
    XunleiTest类:
        这个是测试类,设置了参数:
            URL:          下载的目的地址
            threadCount:  线程数
            DownLoadManager: 创建的下载管理类
            然后调用其download()方法:
                值得一提的是在调用这个方法时,同时实现了OnSizeChanged接口
                这个接口是用来实时显示已经下载了多少数据,即进度是多少,
                采用的是回调函数的形式。
                里面是靠total不断的累加得到的数据和,
                这个total要定义成全局静态的,要不就达不到数据变化的效果

    DownLoadManager类:
        download()方法:
          参数说明   url:要下载的文件的url  threadCount :线程数   Directory:保存的目录位置 OnSizeChanged:下载的数据量
             1,取出文件大小  getFileSize(url):
                    A,通过 openConnection()建立连接
                    B,注意:在此方法中因为只需要知道获得文件的大小,
                      所以setRequestMethod()设置为HEAD
                    C,getResponseCode()获取服务器的响应码
                        要是为200,则表示访问成功
                    D,getContentLength()接受其文件的大小

            2, 将下载的文件重新命名 genFileName():
                A,取到当前的时间到秒
                B,通过url.getFile()取到下载的文件的名字,通过截取的方式保留文件的后缀名

            3,创建文件:
                gen():用来创建一个新的文件带保存地址和名字如果有保存的地址则
                    如果有保存的地址则用有的,如果没有则通过System.getProperty("user.dir");
                    用系统默认的。
                createSaveFile():用来在划分一个空间,用来以后接受下载时接受数据;
                    //RandomAccessFile:此类的实例支持对随机访问文件的读取和写入.
                        随机访问文件的行为类似存储在文件系统中的一个大型 byte 数组
                    //"rwd"   打开以便读取和写入,对于 "rw",还要求对文件内容的每个更新都同步写入到底层存储设备。
                    RandomAccessFile raf=new RandomAccessFile(saveFile, "rwd");

            4:每个线程需要下载的长度 getSizePerThread();
                文件总大小%线程数==0? 文件总大小%线程数:文件总大小/线程数+1

            5,根据线程数threadCount启动线程 DownLoadService
                并将这些参数传过去:
                    URL:下载地址
                      i:  当前线程
          sizePerThread:该线程要下载的长度
               savefile:保存的文件
           onSizeChanged:下载的变化量

    DownLoadService类是一个实现了Runnable接口的一个类:
        run():
            A,计算当前的线程的起始位置  start
            B,计算当前的线程的终点位置  end
            C,建立与资源的连接:
                这里的setRequestMethod()用"GET",因为要正式下载其内容了
                注意:
                    需要设置下载时的长度
                    //设置一般请求属性  键        值
                    setRequestProperty("Range", "bytes="+start+"-"+end)
                    //HTPP属性: Range 字节范围

            D,用字节输入流接受数据  InputStream
            E,创建RandomAccessFile
                注意:为了能点对点的将数据精确插入
                这里使用了RandomAccessFile.seek(start);

            F,进入循环将流写到本地,
            并且其实回传onSizeChanged即显示的进度量

具体代码:

package com.yc.bean3.xunleixaizai;

import java.io.IOException;

import java.net.URL;

public class XunleiTest {

static long total=0;
/**
 * @param args
 * @throws IOException 
 */
public static void main(String[] args) throws IOException {
    URL url=new URL("http://yze.t.sogou.com/installpage/sogougmoh.e?dn=ThunderSpeed_1.0.32.350_XiaZaiBa_sgdl.exe");
    int threadCount=4;
    DownLoadManager dlm=new DownLoadManager();
    dlm.download(url, threadCount, null, new OnSizeChanged(){

        @Override
        public void nofiySizeChange(long size) {
            total+=size;
            System.out.println("已经下载了:"+total+"字节");

        }
    });
}

}

package com.yc.bean3.xunleixaizai;

public interface OnSizeChanged {

public void nofiySizeChange(long size);

}

package com.yc.bean3.xunleixaizai;

import java.io.File;

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

public class DownLoadManager {
//下载方法: url:要下载的文件的url threadCount :线程数 Directory:保存的目录位置
public void download(URL url,int threadCount,File Directory, OnSizeChanged onSizeChanged) throws IOException{
long filesize=-1;

    try {
        //1取出文件大小
         filesize=getFileSize(url);
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    //2,取出文件名
    String filename=genFileName(url);
    //3创建文件
    File savefile=gen(filename, Directory);
    createSaveFile(savefile, filesize);

    //求出每个线程要下载的长度
    long sizePerThread=getSizePerThread(filesize, threadCount);
    //4根据 线程数据启动线程
    for(int i=0;i<threadCount;i++){
        Thread t=new Thread(new DownLoadService(url,i,sizePerThread,savefile,onSizeChanged));
        t.start();
    }


}

//求出每个线程要下载的文件长度
private long getSizePerThread(long filesize,int threadCount){
    return filesize%threadCount==0? filesize/threadCount: filesize/threadCount+1;
}


//获取要下载的文件大小
private long getFileSize(URL url) throws IOException{
    long filesize=-1;
    HttpURLConnection con = (HttpURLConnection) url.openConnection();

    con.setConnectTimeout(40000); //保持与服务器的连接的时间
    con.setRequestMethod("HEAD");//注意:请求方式不同,返回的流的数据不同

    con.connect();

    int code=con.getResponseCode();  //获取服务器的响应码

    if(code==200){
        filesize=con.getContentLength();
    }

    return filesize;
}

//在磁盘上创建保存文件
//saveFile:要保存的文件的全路径
//filesize:文件的大小
private void createSaveFile(File saveFile,long filesize) throws IOException{
    //RandomAccessFile:此类的实例支持对随机访问文件的读取和写入. 随机访问文件的行为类似存储在文件系统中的一个大型 byte 数组
    //"rwd"   打开以便读取和写入,对于 "rw",还要求对文件内容的每个更新都同步写入到底层存储设备。 
    RandomAccessFile raf=new RandomAccessFile(saveFile, "rwd");
    raf.setLength(filesize);
    raf.close();
}

//savefilename:要保存的文件名  
//savedirectory: 要保存的目录,如果为空的话,则系统指定目录
private static File gen(String savefilename, File savedirectory) {
    File f=null;
    if(savedirectory==null){
        f=new File(System.getProperty("user.dir"),savefilename);
    }else{
        f=new File(savedirectory,savefilename);
    }
    return f;
}

//从url中取后缀名  : yyyyMMddHHmmss.原文件的后缀名
private static String genFileName(URL url){


    Date d=new Date();
    SimpleDateFormat sdf=new SimpleDateFormat("yyyyMMddHHmmss");
    String filename=sdf.format(d);

    //从url中取后缀名
    String suffix=url.getFile().substring(url.getFile().lastIndexOf("."));
    filename+=suffix;
    return filename;

}

}

package com.yc.bean3.xunleixaizai;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.ProtocolException;
import java.net.URL;

public class DownLoadService implements Runnable {
private URL url;
private int i;
private long sizePerThread;
private File savefile;
private boolean flag;
private OnSizeChanged onSizeChanged;

public DownLoadService(URL url,int i, long sizePerThread,File savefile, OnSizeChanged onSizeChanged){
    super();
    this.url=url;
    this.i=i;
    this.sizePerThread=sizePerThread;
    this.savefile=savefile;
    flag=true;
    this.onSizeChanged=onSizeChanged;
}




@Override
public void run() {
    //4计算当前的线程的起始位置
    long start=i*sizePerThread;
    //5计算当前的线程的终点位置
    long end=(i+1)*sizePerThread-1;
    InputStream iis=null;
    //1,建立与资源的连接
    HttpURLConnection con;

    try {
        con = (HttpURLConnection) url.openConnection();
        con.setConnectTimeout(40000); //保持与服务器的连接的时间
        con.setRequestMethod("GET");//注意:请求方式不同,返回的流的数据不同
        con.setRequestProperty("Range", "bytes="+start+"-"+end);
        con.connect();
         iis=con.getInputStream();
    } catch (ProtocolException e2) {
        // TODO Auto-generated catch block
        e2.printStackTrace();
    } catch (IOException e2) {
        // TODO Auto-generated catch block
        e2.printStackTrace();
    }
    //2创建输入流

    //3创建RandomAccessFile
    //RandomAccessFile:此类的实例支持对随机访问文件的读取和写入. 随机访问文件的行为类似存储在文件系统中的一个大型 byte 数组
    //"rwd"   打开以便读取和写入,对于 "rw",还要求对文件内容的每个更新都同步写入到底层存储设备。 
    RandomAccessFile raf = null;
    try {
        raf = new RandomAccessFile(savefile, "rwd");
        //给raf定位
        raf.seek(start);//设置到此文件开头测量到的文件指针偏移量,在该位置发生下一个读取或写入操作
    } catch (Exception e2) {
        e2.printStackTrace();
    }

    while(flag){
        byte[] bs=new byte[1024];
        int length=-1;
        try {
            while((length=iis.read(bs,0,bs.length))!=-1){
                raf.write(bs,0,length);
                if(this.onSizeChanged!=null){
                    this.onSizeChanged.nofiySizeChange(length);
                }
            }
            flag=false;
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

}

你可能感兴趣的:(迅雷)