http断点续传

单线程下载HTTP文件是一件非常简单的事。那么,多线程断点需要什么功能?

 1.多线程下载;

 2.支持断点;

 

思路:第一次下载某个文件的时候,创建一个下载线程开始从网络下载文件,把已经下载的部分保存到磁盘文件,当用户通过UI主动暂停下载的时候,把线程停止销毁。

          下次下载同一个url的文件的时候,重新创建一个下载线程开始从网络下载文件,如果发现磁盘上保存过该文件,则通过  HttpURLConnection.setRequestProperty("Range", "bytes=本地缓存的文件的字节数-完整文件的大小");

          从上次中断的地方请求继续下载。

 

一、多线程

使用多线程的好处:使用多线程下载会提升文件下载的速度。那么多线程下载文件的过程是: 

  (1)首先获得下载文件的长度,然后设置本地文件的长度。

       HttpURLConnection.getContentLength();//获取下载文件的长度

       RandomAccessFile file = new RandomAccessFile("QQWubiSetup.exe","rwd");

       file.setLength(filesize);//设置本地文件的长度

 

   (2)根据文件长度和线程数计算每条线程下载的数据长度和下载位置。

      如:文件的长度为6M,线程数为3,那么,每条线程下载的数据长度为2M,每条线程开始下载的位置如下图所示。

   

 

 

   (3)使用Http的Range头字段指定每条线程从文件的什么位置开始下载,下载到什么位置为止,

       比如要下载的文件是1000个字节(编号0-999),分两次下载

       HttpURLConnection.setRequestProperty("Range", "bytes=0-499");  下载文件的前500个字节,0代表第一个字节。

       HttpURLConnection.setRequestProperty("Range", "bytes=500-999");   

       

 

   (4)保存文件,使用RandomAccessFile类指定每条线程从本地文件的什么位置开始写入数据。

       RandomAccessFile threadfile = new RandomAccessFile("QQWubiSetup.exe ","rwd");

       threadfile.seek(2097152);//从文件的什么位置开始写入数据

          

 二、断点续传

写了一个断点续传的组件

public  class  SuspendableDownloader {
     private   static   final  String SDPATH =  "//sdcard//" ;
     private   static   final  String TAG  =  "SuspendableDownloader" ;
     public  boolean  isStopDownload =  false ;
     public  void  startDownload() {
         isStopDownload =  false ;
     }
     public  void  stopDownload(){
         isStopDownload =  true ;
     }
 
     public  interface  CallBack{
         public  boolean  notfiyProgress( int  percent);
         public  void  onDownLoadCancel();
     }
     private    CallBack mCallBack =  null ;
     public   void  setCallBack(CallBack callback){
         this .mCallBack = callback;
     }
 
     public   String downLoadFile(String httpUrl)  throws  IOException {
         File tmpFile =  new  File(SDPATH);
         if  (!tmpFile.exists()) {
             tmpFile.mkdir();
         }
         String fileName = httpUrl.split( "/" )[httpUrl.split( "/" ).length- 1 ];
         final  RandomAccessFile file =  new  RandomAccessFile(SDPATH + fileName, "rwd" );
         //第一次下载
         if  (file.length() ==  0 ) {
             URL url =  new  URL(httpUrl);
             HttpURLConnection conn = (HttpURLConnection) url.openConnection();
             int   length = conn.getContentLength();
             InputStream is = conn.getInputStream();
             //FileOutputStream fos = new FileOutputStream(file);
             byte [] buf =  new  byte [ 256 * 2 ];
             conn.connect();
             double  count =  0 ;
             if  (conn.getResponseCode() >=  400 ) {
                  Log.i(TAG, "time exceed" );
             else  {
                 while  (count <=  100  && !isStopDownload) {
                     if  (is !=  null ) {
                         int  numRead = is.read(buf);
 
                         if  (numRead <=  0 ) {
                             break ;
                         else  {
                             file.write(buf,  0 , numRead);
                             mCallBack.notfiyProgress(( int )(file.length()* 100 /length));
                             Log.d(TAG, "notifyprogress=" +( int )(file.length()* 100 /length));
                         }
                     else  {
                         break ;
                     }
                 }
                 if  (isStopDownload) {
                     mCallBack.onDownLoadCancel();
                 }
             }
             conn.disconnect();
             file.close();
             is.close();
         } else  {
             //不是第一次下载,之前有过下载
             Log.d(TAG, "continue file.length() =" +file.length()  );
         //    file.seek(file.length());
             URL url =  new  URL(httpUrl);
             HttpURLConnection conn = (HttpURLConnection) url.openConnection();
             int  length = conn.getContentLength();
 
             HttpURLConnection conn2 = (HttpURLConnection) url.openConnection();
             conn2.setRequestProperty( "Range" "bytes=" +file.length()+ "-" +(length- 1 ));  //如果文件已经下载完成,即从 1234-1234 会报告FileNotFound Exception
             //java.lang.IllegalStateException: Cannot set request property after connection is made
           //  int length = conn.getContentLength();http://www.eoeandroid.com/thread-154241-1-1.html
             if  (file.length() == length) {
                  return  SDPATH + fileName;
             }
             // file.setLength(length);
             InputStream is = conn.getInputStream();
             //FileOutputStream fos = new FileOutputStream(file);
             byte [] buf =  new  byte [ 256 * 2 ];
             conn.connect();
             double  count =  0 ;
             if  (conn.getResponseCode() >=  400 ) {
                  Log.i(TAG, "time exceed" );
             else  {
                 while  (count <=  100  && !isStopDownload) {
                     if  (is !=  null ) {
                         int  numRead = is.read(buf);
                         if  (numRead <=  0 ) {
                             break ;
                         else  {
                             file.write(buf,  0 , numRead);
                             mCallBack.notfiyProgress(( int )(file.length()* 100 /length));
                             Log.d(TAG, "notifyprogress=" +( int )(file.length()* 100 /length));
                         }
                     else  {
                         break ;
                     }
                 }
                 if  (isStopDownload) {
                     mCallBack.onDownLoadCancel();
                 }
             }
             conn.disconnect();
             file.close();
             is.close();
 
         }
         return  SDPATH + fileName;
     }
}

 

注意一些地方:

       1.错误 java.lang.IllegalStateException: Cannot set request property after connection is made

           由于要通过HttpURLConnection  getContentLength获取下载文件的长度,之后才能设置Range请求头,但是getContentLength已经与server建立了connection,导致了这个错误。解决方法就是通过两个HttpURLConnection实例,第一次获取下载文件长度,第二次获取文件

      2. Thread.stop 已经废弃,建议使用调用interrupt , 但是有些操作是不可打断的。包括进入synchronized段以及Lock.lock(),inputSteam.read()等。解决方法是同一个变量来控制isStopDownload来控制下载线程的退出。


你可能感兴趣的:(http断点续传)