请问如何用java实现ftp的多线程下载,
实现原理是什么?有代码最好

解决方案 »

  1.   

    1:字节流结构:
    每次发送12+1024个字节
    其中,前七个字节字节表示命令
    第七到第十二个字节表示后面1024字节中的有效字节的长度
    例如一帧中前12个字节的内容为"UPFILEN00012"表示要上传文件名,文件名的长度是12。那么程序就在后面的1024个字节中去12个字节,在把它转换为字符串,作为要上传的文件名。2:命令结构
    Server端:
    DISCONN:断开连接
    LSFILES:发送当前目录文件列表
    ENDFILE:上传一个文件的结束标记
    UPFILEN:表示要上传一个新的文件,并且此包中包含了文件名
    UPDATAS:表示本包是要上传的数据
    DNFILEN:表示要下载的文件名,服务器要执行向客户端传输文件的操作
    Client端:
    DISCONN:断开连接
    LSFILES:接收服务器当前目录文件列表
    ENDFILE:下载一个文件的结束标记
    DNDATAS:表示本包是要下载的数据3:文件结构
    FtpServer:ftp软件的服务器端,目前在端口2121监听,支持多线程,文件的上传,下载,列表。
    FtpClient:ftp软件的客户端,默认连接本机的服务器端,在端口2121,支持文件的上传,下载,列表。
    FtpClientUI:ftp软件的客户端的用户界面,完全采用Swing技术,手工编写,没有用JB自动生成。
    PublicFunc:提供一些公共的静态方法例如将给定的String对象分装成要发送的帧。将数字格式化成长度为五位的String类型对象。
    package cn.edu.bit.software.ftptrans;import java.io.*;
    import java.net.*;
    import java.util.Vector;
    import java.util.logging.*;public class FtpServer
    {
      //客户端socket对象
      private ServerSocket m_servSocket;  //ftp服务器的端口号
      private int SERVER_PORT;  //ftp服务器所允许的最大连接数
      private int MAX_CONN;  //连入的客户端处理对象管理器
      private Vector vecClient;  //设定一个log日志对象
      private Logger mylog;  private ConsoleHandler handler;  String strServHome;  public FtpServer(int servPort, int maxConn)
      {
        SERVER_PORT = servPort;
        MAX_CONN = maxConn;
        strServHome = "c:\\";
        vecClient = new Vector();
        /*------------初始化log------------*/
        try
        {
          handler = new ConsoleHandler();
          handler.setLevel(Level.ALL);
          mylog = Logger.getLogger("FtpServer");
          mylog.addHandler(handler);
          mylog.setLevel(Level.ALL);
        }
        catch (SecurityException e)
        {
          mylog.warning("在设置程序日志级别时出现异常");
          mylog.warning(e.getMessage());
        }    /*--------------初始化服务器,端口2121----------------------*/
        try
        {
          m_servSocket = new ServerSocket(SERVER_PORT, MAX_CONN);
          while (true)
          {
            mylog.finest("FtpServer开始在端口2121监听");
            Socket clientSocket = m_servSocket.accept();
            vecClient.add(clientSocket);
            mylog.info("#" + vecClient.size() + "号客户端连入");
            new TransHandler(this, clientSocket, vecClient.size()).start();
          }
        }
        catch (IOException e)
        {
          mylog.warning("在初始化FtpServ时出现错误");
          mylog.warning(e.getMessage());
        }
      }  public void deleteClient(TransHandler handler)
      {
        try
        {
          vecClient.remove(handler);
          vecClient.setSize(vecClient.size() - 1);
          mylog.info("第#" + handler.iClientNum + "号客户端断开了与服务器的连接!");
        }
        catch (Exception e)
        {
          mylog.warning("在删除第#" + handler.iClientNum + "号客户端时出现异常");
          mylog.warning(e.getMessage());
        }
      }  public static void main(String[] args)
      {
        new FtpServer(2121, 50);
      }
    }
      

  2.   

    /**
     * 当有客户端连入时,处理客户端请求的类
     * @author findfrog
     * @version 1.0
     */
    class TransHandler extends Thread
    {
      //服务器句柄,用于最后销毁TransHandler对象时用
      FtpServer main = null;
      //客户端的socket
      private Socket m_clientSocket = null;  //日志对象
      private Logger mylog;  //要上传的文件路径
      private String strUpFilePath = null;  //要下载的文件路径
      private String strDnFilePath = null;  //本客户端在的序号
      int iClientNum = -1;  //缓冲字节数据的大小
      private int ibufferlength;  //缓冲字节数组
      byte[] inputBytes;  //从客户端传来的指令
      String strClientOrder;
      //用于得到从socket端的输入信息
      InputStream m_inputStream;
      //用于向socket输出的输出流
      OutputStream m_outputStream;
      //用于上传文件的输出流
      FileOutputStream m_fileOutputStream;
      //用于下载文件的输入流
      FileInputStream m_fileInputStream;  //构造函数
      public TransHandler(FtpServer fserver, Socket s, int iNum)
      {
        try
        {
          main = fserver;
          //将客户端socket句柄付给本地对象
          m_clientSocket = s;
          //初始化log对象
          mylog = Logger.getLogger("TransHandler");
          //初始化本客户端序号
          iClientNum = iNum;
          //用于得到从socket端的输入信息
          m_inputStream = m_clientSocket.getInputStream();
          m_outputStream = m_clientSocket.getOutputStream();
          ibufferlength = 1024;
          inputBytes = new byte[ibufferlength + 12];    }
        catch (Exception e)
        {
          mylog.warning("在初始化TransHandler时发生异常!");
          mylog.warning(e.getMessage());
        }
      }  public void run()
      {
        try
        {
          int ilength;
          while ( (ilength = m_inputStream.read(inputBytes, 0, 12 + ibufferlength)) !=
                 -1)
          {
            strClientOrder = new String(inputBytes, 0, 7);
            if (strClientOrder.equals("DISCONN"))
            { //断开连接
              mylog.info("得到了DISCONN");
              exit();
            }
            else if (strClientOrder.equals("LSFILES"))
            { //发送当前目录文件列表
              mylog.info("服务器端接收到了LSFILES命令");
              File flHome = new File(main.strServHome);
              String[] strFileNames = flHome.list();
              strFileNames = AdjustStrings(strFileNames);
              for (int i = 0; i < strFileNames.length; i++)
              {
                String strFileNameLength = PublicFunc.formatLength(strFileNames[i].
                    getBytes().length);
                byte[] fileNameBytes = strFileNames[i].getBytes();
                byte[] outBytes = PublicFunc.makepackage("LSFILES",
                    strFileNameLength, fileNameBytes);
                m_outputStream.write(outBytes, 0, outBytes.length);
                m_outputStream.flush();
              }
            }
            else if (strClientOrder.equals("ENDFILE"))
            { //上传一个文件的结束标记
              mylog.info("收到文件结束标志符号");
              m_fileOutputStream.close();
            }
            else if (strClientOrder.equals("UPFILEN"))
            { //表示要上传一个新的文件,并且此包中包含了文件名
              int iFileNameLength = Integer.parseInt(new String(inputBytes, 7, 5));
              mylog.info("要上传的文件名的长度为" + iFileNameLength);
              String strFileName = new String(inputBytes, 12, iFileNameLength);
              mylog.info("要上传的文件名是:" + strFileName);
              //初始化上传文件路径
              strUpFilePath = main.strServHome + strFileName;
              File upFile = new File(strUpFilePath);
              m_fileOutputStream = new FileOutputStream(upFile);
            }
            else if (strClientOrder.equals("UPDATAS"))
            { //表示本包是要上传的数据
              //本次数据包的长度
              mylog.info("正在接收文件...");
              int iDataLength = Integer.parseInt(new String(inputBytes, 7, 5));
              m_fileOutputStream.write(inputBytes, 12, iDataLength);
              m_fileOutputStream.flush();
            }
            else if (strClientOrder.equals("DNFILEN"))
            { //表示要下载的文件名,服务器要执行向客户端传输文件的操作
              int iFileNameLength = Integer.parseInt(new String(inputBytes, 7, 5));
              mylog.info("要下载的文件名的长度为" + iFileNameLength);
              String strFileName = new String(inputBytes, 12, iFileNameLength);
              mylog.info("要下载的文件名是:" + strFileName);
              //初始化上传文件路径
              strDnFilePath = main.strServHome + strFileName;
              File dnFile = new File(strDnFilePath);
              //初始化了文件输出流
              m_fileInputStream = new FileInputStream(dnFile);          //开始向客户端传输文件
              mylog.info("开始向客户端传输文件" + strFileName + "...");
              int iInputLength = 0;
              String strInputLength;
              byte[] readBytes = new byte[ibufferlength];
              while ( (iInputLength = m_fileInputStream.read(readBytes, 0,
                  ibufferlength)) !=
                     -1)
              {
                strInputLength = PublicFunc.formatLength(iInputLength);
                byte[] outBytes = PublicFunc.makepackage("DNDATAS", strInputLength,
                    readBytes);
                m_outputStream.write(outBytes, 0, outBytes.length);
                m_outputStream.flush();
              }          //最后发送一个文件结束标记
              m_outputStream.write(PublicFunc.makepackage("ENDFILE", "00001",
                  new byte[1]));
              m_outputStream.flush();
            }
          }
        }
        catch (Exception e)
        {
          mylog.warning(e.getMessage());
        }
      }  public void exit()
      {
        try
        {
          m_outputStream.write(PublicFunc.makepackage("DISCONN", "00001",
                                                      new byte[1]));
          m_inputStream.close();
          m_outputStream.close();
          main.deleteClient(this);
          main = null;
        }
        catch (Exception e)
        {
          mylog.warning("在断开客户端#" + this.iClientNum + "连接时出现异常!");
          mylog.warning(e.getMessage());
        }
      }  public String[] AdjustStrings(String[] strFileNames)
      {
        String[] strItemNames = new String[strFileNames.length + 1];
        strItemNames[0] = "返回上一级";
        int j = 1;
        for (int i = 0; i < strFileNames.length; i++)
        {
          File upFile = new File(main.strServHome + strFileNames[i]);
          if (!upFile.isFile())
          {
            strItemNames[j++] = "[文件夹]" + strFileNames[i];
          }
        }
        for (int i = 0; i < strFileNames.length; i++)
        {
          File upFile = new File(main.strServHome + strFileNames[i]);
          if (upFile.isFile())
          {
            strItemNames[j++] = strFileNames[i];
          }
        }    return strItemNames;
      }}
      

  3.   

    package cn.edu.bit.software.ftptrans;import java.io.*;
    import java.net.*;
    import java.util.logging.*;
    import java.util.Vector;public class FtpClient extends Thread
    {
      Logger mylog = Logger.getLogger("FtpClient");
      Socket m_clientSocket;
      //缓冲字节数据的大小
      private int ibufferlength;  //缓冲字节数组
      byte[] inputBytes;  Vector vecServFiles;  //用于得到从socket端的输入信息
      InputStream m_inputStream;
      //用于向socket输出的输出流
      OutputStream m_outputStream;
      //向本地写文件的文件输出流
      FileOutputStream m_fileOutputStream;
      //从本地读文件的文件输入流
      FileInputStream m_fileInputStream;
      //从服务器端传来的指令
      String strServerOrder;
      //主机的ip地址
      String strServerIP;
      //服务器的端口号
      int iServerPort;  public FtpClient(String strServIP, int iServPort)
      {
        strServerIP = strServIP;
        iServerPort = iServPort;
      }  public void run()
      {
        try
        {
          //建立连接
          m_clientSocket = new Socket(strServerIP, iServerPort);
          mylog.info("已经连到了主机" + strServerIP + "在端口" + iServerPort);
          m_inputStream = m_clientSocket.getInputStream();
          m_outputStream = m_clientSocket.getOutputStream();
          mylog.fine("客户端得到了socket的输入输出流!");
          ibufferlength = 1024;
          inputBytes = new byte[ibufferlength + 12];
          vecServFiles = new Vector();
          doBusiness();
        }
        catch (UnknownHostException e)
        {
          mylog.warning("服务器地址未知");
          mylog.warning(e.getMessage());
        }
        catch (IOException e)
        {
          mylog.warning(e.getMessage());
        }
        catch (Exception e)
        {
          mylog.warning(e.getMessage());
        }
      }  public void doBusiness()
      {
        try
        {
          int iLength = 0;
          while ( (iLength = m_inputStream.read(inputBytes, 0, ibufferlength + 12)) !=
                 -1)
          {
            strServerOrder = new String(inputBytes, 0, 7);
            if (strServerOrder.equals("DISCONN"))
            { //断开连接
              mylog.info("在client端得到了DISCONN");
              int length = Integer.parseInt(new String(inputBytes, 7, 5));
              mylog.info("长度是" + length);
            }
            else if (strServerOrder.equals("LSFILES"))
            { //接收服务器当前目录文件列表          int iDataLength = Integer.parseInt(new String(inputBytes, 7, 5));
              mylog.info("在客户端这个文件名的长度是:" + iDataLength);
              String strFileName = new String(inputBytes, 12, iDataLength);
              mylog.info("客户端正在获取服务器目录信息....." + strFileName);
              vecServFiles.add(strFileName);
            }
            else if (strServerOrder.equals("ENDFILE"))
            { //下载一个文件的结束标记
              mylog.info("收到下载文件结束标志符号");
              m_fileOutputStream.close();
            }
            else if (strServerOrder.equals("DNDATAS"))
            { //表示本包是要下载的数据
              int iDataLength = Integer.parseInt(new String(inputBytes, 7, 5));
              m_fileOutputStream.write(inputBytes, 12, iDataLength);
              m_fileOutputStream.flush();
            }
          }
        }
        catch (Exception e)
        {    }
      }  /**
       * 客户端上传文件名
       * @param strFileName 要上传文件的文件名
       */
      public void upFileName(String strFileName)
      {
        try
        {
          String strLength = PublicFunc.formatLength(strFileName.getBytes().length);
          byte[] outBytes = PublicFunc.makepackage("UPFILEN", strLength,
                                                   strFileName.getBytes());
          m_outputStream.write(outBytes, 0, outBytes.length);
          m_outputStream.flush();
        }
        catch (Exception e)
        {
          mylog.warning("在客户端在向服务器写要上传的文件名时发生异常");
          mylog.warning(e.getMessage());
        }
      }  /**
       * 讲本地文件strFilePath上传到服务器
       * @param strFilePath 本地文件路径
       */
      public void upFileData(String strFilePath)
      {
        try
        {
          File file = new File(strFilePath);
          m_fileInputStream = new FileInputStream(file);      int iInputLength = 0;
          String strInputLength;
          byte[] readBytes = new byte[ibufferlength];
          while ( (iInputLength = m_fileInputStream.read(readBytes, 0,
              ibufferlength)) !=
                 -1)
          {
            strInputLength = PublicFunc.formatLength(iInputLength);
            byte[] outBytes = PublicFunc.makepackage("UPDATAS", strInputLength,
                                                     readBytes);
            m_outputStream.write(outBytes, 0, outBytes.length);
            m_outputStream.flush();
          }
          //最后发送一个文件结束标记
          m_outputStream.write(PublicFunc.makepackage("ENDFILE", "00001",
                                                      new byte[1]));
          m_outputStream.flush();
        }
        catch (Exception e)
        {
          mylog.warning("从客户端向服务器传输文件内容是发生异常");
          mylog.warning(e.getMessage());
        }
      }
      

  4.   

    非常感谢 OnlyFor_love  
    但是我需要的是 从已有的 ftp服务器 来实现 多线程下载
    并不是自己写一个服务器端程序请问有什么思路吗?
    谢谢
      

  5.   

    用Java实现HTTP断点续传
    (一)断点续传的原理   其实断点续传的原理很简单,就是在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; Windows NT 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(" 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 = new RandomAccessFile("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.   

    (三)断点续传内核的实现  主要用了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) throws IOException
    {
     siteInfoBean = bean;
     //tmpFile = File.createTempFile ("zhong","1111",new File(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("File Length is not known!");
       }
       else if(nFileLength == -2)
       {
        System.err.println("File is not access!");
       }
      else
      {
       for(int i=0;i<nStartPos.length;i++)
       {
        nStartPos[i] = (long)(i*(nFileLength/nStartPos.length));
       }
       for(int i=0;i<nEndPos.length-1;i++)
       {
        nEndPos[i] = nStartPos[i+1];
       }
       nEndPos[nEndPos.length-1] = nFileLength;
      }
     } //启动子线程
     fileSplitterFetch = new FileSplitterFetch[nStartPos.length];
     for(int i=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] = new FileSplitterFetch(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(); //等待子线程结束
     //int count = 0;
     //是否结束while循环
     boolean breakWhile = false; while(!bStop)
     {
      write_nPos();
      Utility.sleep(500);
      breakWhile = true;  for(int i=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()
    {
     int nFileLength = -1;
     try{
      URL url = new URL(siteInfoBean.getSSiteURL());
      HttpURLConnection httpConnection = (HttpURLConnection)url.openConnection ();
      httpConnection.setRequestProperty("User-Agent","NetFox");  int responseCode=httpConnection.getResponseCode();
      if(responseCode>=400)
      {
       processErrorCode(responseCode);
       return -2; //-2 represent access is error
      }  String sHeader;  for(int i=1;;i++)
      {
       //DataInputStream in = new DataInputStream(httpConnection.getInputStream ());
       //Utility.log(in.readLine());
       sHeader=httpConnection.getHeaderFieldKey(i);
       if(sHeader!=null)
       {
        if(sHeader.equals("Content-Length"))
        {
         nFileLength = Integer.parseInt(httpConnection.getHeaderField(sHeader));
         break;
        }
       }
       else
       break;
      }
     }
     catch(IOException e){e.printStackTrace ();}
     catch(Exception e){e.printStackTrace ();} Utility.log(nFileLength);
     return nFileLength;
    }//保存下载信息(文件指针位置)
    private void write_nPos()
    {
     try{
      output = new DataOutputStream(new FileOutputStream(tmpFile));
      output.writeInt(nStartPos.length);
      for(int i=0;i<nStartPos.length;i++)
      {
       // output.writeLong(nPos[i]);
       output.writeLong(fileSplitterFetch[i].nStartPos);
       output.writeLong(fileSplitterFetch[i].nEndPos);
      }
      output.close();
     }
     catch(IOException e){e.printStackTrace ();}
     catch(Exception e){e.printStackTrace ();}
    }//读取保存的下载信息(文件指针位置)
    private void read_nPos()
    {
     try{
      DataInputStream input = new DataInputStream(new FileInputStream(tmpFile));
      int nCount = input.readInt();
      nStartPos = new long[nCount];
      nEndPos = new long[nCount];
      for(int i=0;i<nStartPos.length;i++)
      {
       nStartPos[i] = input.readLong();
       nEndPos[i] = input.readLong();
      }
      input.close();
     }
     catch(IOException e){e.printStackTrace ();}
     catch(Exception e){e.printStackTrace ();}
    }private void processErrorCode(int nErrorCode)
    {
     System.err.println("Error Code : " + nErrorCode);
    }//停止文件下载
    public void siteStop()
    {
     bStop = true;
     for(int i=0;i<nStartPos.length;i++)
      fileSplitterFetch[i].splitterStop();}
    }
    /*
    **FileSplitterFetch.java
    */
    package NetFox;import java.io.*;
    import java.net.*;
      

  7.   

    public class FileSplitterFetch extends Thread { String sURL; //File URL
     long nStartPos; //File Snippet Start Position
     long nEndPos; //File Snippet End Position
     int nThreadID; //Thread's ID
     boolean bDownOver = false; //Downing is over
     boolean bStop = false; //Stop identical
     FileAccessI fileAccessI = null; //File Access interface public FileSplitterFetch(String sURL,String sName,long nStart,long nEnd,int id) throws IOException
     {
      this.sURL = sURL;
      this.nStartPos = nStart;
      this.nEndPos = nEnd;
      nThreadID = id;
      fileAccessI = new FileAccessI(sName,nStartPos);
     }public void run()
    {
     while(nStartPos < nEndPos && !bStop)
     {
      try{
       URL url = new URL(sURL);
       HttpURLConnection httpConnection = (HttpURLConnection)url.openConnection ();
       httpConnection.setRequestProperty("User-Agent","NetFox");
       String sProperty = "bytes="+nStartPos+"-";
       httpConnection.setRequestProperty("RANGE",sProperty);
       Utility.log(sProperty);
       InputStream input = httpConnection.getInputStream();
       //logResponseHead(httpConnection);   byte[] b = new byte[1024];
       int nRead;
       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(Exception e){e.printStackTrace ();}
     }
    }//打印回应的头信息
    public void logResponseHead(HttpURLConnection con)
    {
     for(int i=1;;i++)
     {
      String header=con.getHeaderFieldKey(i);
      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{ RandomAccessFile oSavedFile;
     long nPos; public FileAccessI() throws IOException
     {
      this("",0);
     } public FileAccessI(String sName,long nPos) throws IOException
     {
      oSavedFile = new RandomAccessFile(sName,"rw");
      this.nPos = nPos;
      oSavedFile.seek(nPos);
     } public synchronized int write(byte[] b,int nStart,int nLen)
     {
      int n = -1;
      try{
       oSavedFile.write(b,nStart,nLen);
       n = nLen;
      }
      catch(IOException e)
      {
       e.printStackTrace ();
      }  return n;
     }
    }/*
    **SiteInfoBean.java
    */
    package NetFox;public class SiteInfoBean {
     private String sSiteURL; //Site's URL
     private String sFilePath; //Saved File's Path
     private String sFileName; //Saved File's Name
     private int nSplitter; //Count of Splited Downloading File public SiteInfoBean()
     {
      //default value of nSplitter is 5
      this("","","",5);
     } public SiteInfoBean(String sURL,String sPath,String sName,int nSpiltter)
     {
      sSiteURL= sURL;
      sFilePath = sPath;
      sFileName = sName;
      this.nSplitter = nSpiltter;
     } public String getSSiteURL()
     {
      return sSiteURL;
     } public void setSSiteURL(String value)
     {
      sSiteURL = value;
     } public String getSFilePath()
     {
      return sFilePath;
     } public void setSFilePath(String value)
     {
      sFilePath = value;
     } public String getSFileName()
     {
      return sFileName;
     } public void setSFileName(String value)
     {
      sFileName = value;
     } public int getNSplitter()
     {
      return nSplitter;
     } public void setNSplitter(int nCount)
     {
      nSplitter = nCount;
     }
    }/*
    **Utility.java
    */
    package NetFox;
    public class Utility {
     public Utility()
     {
     }
     public static void sleep(int nSecond)
     {
      try{
       Thread.sleep(nSecond);
      }
     catch(Exception e)
     {
      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 {
     public TestMethod()
     { ///xx/weblogic60b2_win.exe
      try{
    SiteInfoBean bean = new SiteInfoBean("http://localhost/xx/weblogic60b2_win.exe";;,"L:\temp","weblogic60b2_win.exe",5);
       //SiteInfoBean bean = new SiteInfoBean("   http://localhost:8080/down.zip";;,"L:\temp","weblogic60b2_win.exe",5);
       SiteFileFetch fileFetch = new SiteFileFetch(bean);
       fileFetch.start();
      }
      catch(Exception e){e.printStackTrace ();}
     }
     public static void main(String[] args)
     {
      new TestMethod();
     }
    }