欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android通过HTTP协议实现断点续传下载实例

程序员文章站 2023-10-08 23:45:49
整理文档,搜刮出一个android通过http协议实现断点续传下载的代码,稍微整理精简一下做下分享。 filedownloader.java  &...

整理文档,搜刮出一个android通过http协议实现断点续传下载的代码,稍微整理精简一下做下分享。

filedownloader.java                                                                                                                

package cn.itcast.net.download; 
import java.io.file; 
import java.io.randomaccessfile; 
import java.net.httpurlconnection; 
import java.net.url; 
import java.util.linkedhashmap; 
import java.util.map; 
import java.util.uuid; 
import java.util.concurrent.concurrenthashmap; 
import java.util.regex.matcher; 
import java.util.regex.pattern; 
import cn.itcast.service.fileservice; 
 
import android.content.context; 
import android.util.log; 
/** 
 * 文件下载器 
 * filedownloader loader = new filedownloader(context, "http://browse.babasport.com/ejb3/activeport.exe", 
        new file("d:\\androidsoft\\test"), 2); 
    loader.getfilesize();//得到文件总大小 
    try { 
      loader.download(new downloadprogresslistener(){ 
        public void ondownloadsize(int size) { 
          print("已经下载:"+ size); 
        }      
      }); 
    } catch (exception e) { 
      e.printstacktrace(); 
    } 
 */ 
public class filedownloader { 
  private static final string tag = "filedownloader"; 
  private context context; 
  private fileservice fileservice;   
  /* 已下载文件长度 */ 
  private int downloadsize = 0; 
  /* 原始文件长度 */ 
  private int filesize = 0; 
  /* 线程数 */ 
  private downloadthread[] threads; 
  /* 本地保存文件 */ 
  private file savefile; 
  /* 缓存各线程下载的长度*/ 
  private map<integer, integer> data = new concurrenthashmap<integer, integer>(); 
  /* 每条线程下载的长度 */ 
  private int block; 
  /* 下载路径 */ 
  private string downloadurl; 
  /** 
   * 获取线程数 
   */ 
  public int getthreadsize() { 
    return threads.length; 
  } 
  /** 
   * 获取文件大小 
   * @return 
   */ 
  public int getfilesize() { 
    return filesize; 
  } 
  /** 
   * 累计已下载大小 
   * @param size 
   */ 
  protected synchronized void append(int size) { 
    downloadsize += size; 
  } 
  /** 
   * 更新指定线程最后下载的位置 
   * @param threadid 线程id 
   * @param pos 最后下载的位置 
   */ 
  protected synchronized void update(int threadid, int pos) { 
    this.data.put(threadid, pos); 
    this.fileservice.update(this.downloadurl, this.data); 
  } 
  /** 
   * 构建文件下载器 
   * @param downloadurl 下载路径 
   * @param filesavedir 文件保存目录 
   * @param threadnum 下载线程数 
   */ 
  public filedownloader(context context, string downloadurl, file filesavedir, int threadnum) { 
    try { 
      this.context = context; 
      this.downloadurl = downloadurl; 
      fileservice = new fileservice(this.context); 
      url url = new url(this.downloadurl); 
      if(!filesavedir.exists()) filesavedir.mkdirs(); 
      this.threads = new downloadthread[threadnum];           
      httpurlconnection conn = (httpurlconnection) url.openconnection(); 
      conn.setconnecttimeout(5*1000); 
      conn.setrequestmethod("get"); 
      conn.setrequestproperty("accept", "image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*"); 
      conn.setrequestproperty("accept-language", "zh-cn"); 
      conn.setrequestproperty("referer", downloadurl);  
      conn.setrequestproperty("charset", "utf-8"); 
      conn.setrequestproperty("user-agent", "mozilla/4.0 (compatible; msie 8.0; windows nt 5.2; trident/4.0; .net clr 1.1.4322; .net clr 2.0.50727; .net clr 3.0.04506.30; .net clr 3.0.4506.2152; .net clr 3.5.30729)"); 
      conn.setrequestproperty("connection", "keep-alive"); 
      conn.connect(); 
      printresponseheader(conn); 
      if (conn.getresponsecode()==200) { 
        this.filesize = conn.getcontentlength();//根据响应获取文件大小 
        if (this.filesize <= 0) throw new runtimeexception("unkown file size "); 
             
        string filename = getfilename(conn);//获取文件名称 
        this.savefile = new file(filesavedir, filename);//构建保存文件 
        map<integer, integer> logdata = fileservice.getdata(downloadurl);//获取下载记录 
        if(logdata.size()>0){//如果存在下载记录 
          for(map.entry<integer, integer> entry : logdata.entryset()) 
            data.put(entry.getkey(), entry.getvalue());//把各条线程已经下载的数据长度放入data中 
        } 
        if(this.data.size()==this.threads.length){//下面计算所有线程已经下载的数据长度 
          for (int i = 0; i < this.threads.length; i++) { 
            this.downloadsize += this.data.get(i+1); 
          } 
          print("已经下载的长度"+ this.downloadsize); 
        } 
        //计算每条线程下载的数据长度 
        this.block = (this.filesize % this.threads.length)==0? this.filesize / this.threads.length : this.filesize / this.threads.length + 1; 
      }else{ 
        throw new runtimeexception("server no response "); 
      } 
    } catch (exception e) { 
      print(e.tostring()); 
      throw new runtimeexception("don't connection this url"); 
    } 
  } 
  /** 
   * 获取文件名 
   */ 
  private string getfilename(httpurlconnection conn) { 
    string filename = this.downloadurl.substring(this.downloadurl.lastindexof('/') + 1); 
    if(filename==null || "".equals(filename.trim())){//如果获取不到文件名称 
      for (int i = 0;; i++) { 
        string mine = conn.getheaderfield(i); 
        if (mine == null) break; 
        if("content-disposition".equals(conn.getheaderfieldkey(i).tolowercase())){ 
          matcher m = pattern.compile(".*filename=(.*)").matcher(mine.tolowercase()); 
          if(m.find()) return m.group(1); 
        } 
      } 
      filename = uuid.randomuuid()+ ".tmp";//默认取一个文件名 
    } 
    return filename; 
  } 
   
  /** 
   * 开始下载文件 
   * @param listener 监听下载数量的变化,如果不需要了解实时下载的数量,可以设置为null 
   * @return 已下载文件大小 
   * @throws exception 
   */ 
  public int download(downloadprogresslistener listener) throws exception{ 
    try { 
      randomaccessfile randout = new randomaccessfile(this.savefile, "rw"); 
      if(this.filesize>0) randout.setlength(this.filesize); 
      randout.close(); 
      url url = new url(this.downloadurl); 
      if(this.data.size() != this.threads.length){ 
        this.data.clear(); 
        for (int i = 0; i < this.threads.length; i++) { 
          this.data.put(i+1, 0);//初始化每条线程已经下载的数据长度为0 
        } 
      } 
      for (int i = 0; i < this.threads.length; i++) {//开启线程进行下载 
        int downlength = this.data.get(i+1); 
        if(downlength < this.block && this.downloadsize<this.filesize){//判断线程是否已经完成下载,否则继续下载  
          this.threads[i] = new downloadthread(this, url, this.savefile, this.block, this.data.get(i+1), i+1); 
          this.threads[i].setpriority(7); 
          this.threads[i].start(); 
        }else{ 
          this.threads[i] = null; 
        } 
      } 
      this.fileservice.save(this.downloadurl, this.data); 
      boolean notfinish = true;//下载未完成 
      while (notfinish) {// 循环判断所有线程是否完成下载 
        thread.sleep(900); 
        notfinish = false;//假定全部线程下载完成 
        for (int i = 0; i < this.threads.length; i++){ 
          if (this.threads[i] != null && !this.threads[i].isfinish()) {//如果发现线程未完成下载 
            notfinish = true;//设置标志为下载没有完成 
            if(this.threads[i].getdownlength() == -1){//如果下载失败,再重新下载 
              this.threads[i] = new downloadthread(this, url, this.savefile, this.block, this.data.get(i+1), i+1); 
              this.threads[i].setpriority(7); 
              this.threads[i].start(); 
            } 
          } 
        }         
        if(listener!=null) listener.ondownloadsize(this.downloadsize);//通知目前已经下载完成的数据长度 
      } 
      fileservice.delete(this.downloadurl); 
    } catch (exception e) { 
      print(e.tostring()); 
      throw new exception("file download fail"); 
    } 
    return this.downloadsize; 
  } 
  /** 
   * 获取http响应头字段 
   * @param http 
   * @return 
   */ 
  public static map<string, string> gethttpresponseheader(httpurlconnection http) { 
    map<string, string> header = new linkedhashmap<string, string>(); 
    for (int i = 0;; i++) { 
      string mine = http.getheaderfield(i); 
      if (mine == null) break; 
      header.put(http.getheaderfieldkey(i), mine); 
    } 
    return header; 
  } 
  /** 
   * 打印http头字段 
   * @param http 
   */ 
  public static void printresponseheader(httpurlconnection http){ 
    map<string, string> header = gethttpresponseheader(http); 
    for(map.entry<string, string> entry : header.entryset()){ 
      string key = entry.getkey()!=null ? entry.getkey()+ ":" : ""; 
      print(key+ entry.getvalue()); 
    } 
  } 
 
  private static void print(string msg){ 
    log.i(tag, msg); 
  } 
} 

downloadthread.java

package cn.itcast.net.download; 
 
import java.io.file; 
import java.io.inputstream; 
import java.io.randomaccessfile; 
import java.net.httpurlconnection; 
import java.net.url; 
 
import android.util.log; 
 
public class downloadthread extends thread { 
  private static final string tag = "downloadthread"; 
  private file savefile; 
  private url downurl; 
  private int block; 
  /* 下载开始位置 */ 
  private int threadid = -1;  
  private int downlength; 
  private boolean finish = false; 
  private filedownloader downloader; 
 
  public downloadthread(filedownloader downloader, url downurl, file savefile, int block, int downlength, int threadid) { 
    this.downurl = downurl; 
    this.savefile = savefile; 
    this.block = block; 
    this.downloader = downloader; 
    this.threadid = threadid; 
    this.downlength = downlength; 
  } 
   
  @override 
  public void run() { 
    if(downlength < block){//未下载完成 
      try { 
        httpurlconnection http = (httpurlconnection) downurl.openconnection(); 
        http.setconnecttimeout(5 * 1000); 
        http.setrequestmethod("get"); 
        http.setrequestproperty("accept", "image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*"); 
        http.setrequestproperty("accept-language", "zh-cn"); 
        http.setrequestproperty("referer", downurl.tostring());  
        http.setrequestproperty("charset", "utf-8"); 
        int startpos = block * (threadid - 1) + downlength;//开始位置 
        int endpos = block * threadid -1;//结束位置 
        http.setrequestproperty("range", "bytes=" + startpos + "-"+ endpos);//设置获取实体数据的范围 
        http.setrequestproperty("user-agent", "mozilla/4.0 (compatible; msie 8.0; windows nt 5.2; trident/4.0; .net clr 1.1.4322; .net clr 2.0.50727; .net clr 3.0.04506.30; .net clr 3.0.4506.2152; .net clr 3.5.30729)"); 
        http.setrequestproperty("connection", "keep-alive"); 
         
        inputstream instream = http.getinputstream(); 
        byte[] buffer = new byte[1024]; 
        int offset = 0; 
        print("thread " + this.threadid + " start download from position "+ startpos); 
        randomaccessfile threadfile = new randomaccessfile(this.savefile, "rwd"); 
        threadfile.seek(startpos); 
        while ((offset = instream.read(buffer, 0, 1024)) != -1) { 
          threadfile.write(buffer, 0, offset); 
          downlength += offset; 
          downloader.update(this.threadid, downlength); 
          downloader.append(offset); 
        } 
        threadfile.close(); 
        instream.close(); 
        print("thread " + this.threadid + " download finish"); 
        this.finish = true; 
      } catch (exception e) { 
        this.downlength = -1; 
        print("thread "+ this.threadid+ ":"+ e); 
      } 
    } 
  } 
  private static void print(string msg){ 
    log.i(tag, msg); 
  } 
  /** 
   * 下载是否完成 
   * @return 
   */ 
  public boolean isfinish() { 
    return finish; 
  } 
  /** 
   * 已经下载的内容大小 
   * @return 如果返回值为-1,代表下载失败 
   */ 
  public long getdownlength() { 
    return downlength; 
  } 
} 

downloadprogresslistener.java

package cn.itcast.net.download; 
 
public interface downloadprogresslistener { 
  public void ondownloadsize(int size); 
} 

dbopenhelper.java

package cn.itcast.service; 
 
import android.content.context; 
import android.database.sqlite.sqlitedatabase; 
import android.database.sqlite.sqliteopenhelper; 
 
public class dbopenhelper extends sqliteopenhelper { 
  private static final string dbname = "itcast.db"; 
  private static final int version = 1; 
   
  public dbopenhelper(context context) { 
    super(context, dbname, null, version); 
  } 
   
  @override 
  public void oncreate(sqlitedatabase db) { 
    db.execsql("create table if not exists filedownlog (id integer primary key autoincrement, downpath varchar(100), threadid integer, downlength integer)"); 
  } 
 
  @override 
  public void onupgrade(sqlitedatabase db, int oldversion, int newversion) { 
    db.execsql("drop table if exists filedownlog"); 
    oncreate(db); 
  } 
 
} 

fileservice.java

package cn.itcast.service; 
 
import java.util.hashmap; 
import java.util.map; 
 
import android.content.context; 
import android.database.cursor; 
import android.database.sqlite.sqlitedatabase; 
/** 
 * 业务bean 
 * 
 */ 
public class fileservice { 
  private dbopenhelper openhelper; 
 
  public fileservice(context context) { 
    openhelper = new dbopenhelper(context); 
  } 
  /** 
   * 获取每条线程已经下载的文件长度 
   * @param path 
   * @return 
   */ 
  public map<integer, integer> getdata(string path){ 
    sqlitedatabase db = openhelper.getreadabledatabase(); 
    cursor cursor = db.rawquery("select threadid, downlength from filedownlog where downpath=?", new string[]{path}); 
    map<integer, integer> data = new hashmap<integer, integer>(); 
    while(cursor.movetonext()){ 
      data.put(cursor.getint(0), cursor.getint(1)); 
    } 
    cursor.close(); 
    db.close(); 
    return data; 
  } 
  /** 
   * 保存每条线程已经下载的文件长度 
   * @param path 
   * @param map 
   */ 
  public void save(string path, map<integer, integer> map){//int threadid, int position 
    sqlitedatabase db = openhelper.getwritabledatabase(); 
    db.begintransaction(); 
    try{ 
      for(map.entry<integer, integer> entry : map.entryset()){ 
        db.execsql("insert into filedownlog(downpath, threadid, downlength) values(?,?,?)", 
            new object[]{path, entry.getkey(), entry.getvalue()}); 
      } 
      db.settransactionsuccessful(); 
    }finally{ 
      db.endtransaction(); 
    } 
    db.close(); 
  } 
  /** 
   * 实时更新每条线程已经下载的文件长度 
   * @param path 
   * @param map 
   */ 
  public void update(string path, map<integer, integer> map){ 
    sqlitedatabase db = openhelper.getwritabledatabase(); 
    db.begintransaction(); 
    try{ 
      for(map.entry<integer, integer> entry : map.entryset()){ 
        db.execsql("update filedownlog set downlength=? where downpath=? and threadid=?", 
            new object[]{entry.getvalue(), path, entry.getkey()}); 
      } 
      db.settransactionsuccessful(); 
    }finally{ 
      db.endtransaction(); 
    } 
    db.close(); 
  } 
  /** 
   * 当文件下载完成后,删除对应的下载记录 
   * @param path 
   */ 
  public void delete(string path){ 
    sqlitedatabase db = openhelper.getwritabledatabase(); 
    db.execsql("delete from filedownlog where downpath=?", new object[]{path}); 
    db.close(); 
  } 
   
} 

downloadactivity.java

package cn.itcast.download; 
 
import java.io.file; 
 
import cn.itcast.net.download.downloadprogresslistener; 
import cn.itcast.net.download.filedownloader; 
 
import android.app.activity; 
import android.os.bundle; 
import android.os.environment; 
import android.os.handler; 
import android.os.message; 
import android.view.view; 
import android.widget.button; 
import android.widget.edittext; 
import android.widget.progressbar; 
import android.widget.textview; 
import android.widget.toast; 
 
public class downloadactivity extends activity { 
  private edittext downloadpathtext; 
  private textview resultview; 
  private progressbar progressbar; 
  //当handler被创建会关联到创建它的当前线程的消息队列,该类用于往消息队列发送消息 
  //消息队列中的消息由当前线程内部进行处理 
  private handler handler = new handler(){ 
 
    @override 
    public void handlemessage(message msg) {       
      switch (msg.what) { 
      case 1:        
        progressbar.setprogress(msg.getdata().getint("size")); 
        float num = (float)progressbar.getprogress()/(float)progressbar.getmax(); 
        int result = (int)(num*100); 
        resultview.settext(result+ "%"); 
        if(progressbar.getprogress()==progressbar.getmax()){ 
          toast.maketext(downloadactivity.this, r.string.success, 1).show(); 
        } 
        break; 
 
      case -1: 
        toast.maketext(downloadactivity.this, r.string.error, 1).show(); 
        break; 
      } 
    } 
  }; 
   
  @override 
  public void oncreate(bundle savedinstancestate) { 
    super.oncreate(savedinstancestate); 
    setcontentview(r.layout.main); 
     
    downloadpathtext = (edittext) this.findviewbyid(r.id.downloadpath); 
    progressbar = (progressbar) this.findviewbyid(r.id.downloadbar); 
    resultview = (textview) this.findviewbyid(r.id.result); 
    button button = (button) this.findviewbyid(r.id.button); 
    button.setonclicklistener(new view.onclicklistener() {      
      @override 
      public void onclick(view v) { 
        string path = downloadpathtext.gettext().tostring(); 
        if(environment.getexternalstoragestate().equals(environment.media_mounted)){ 
          download(path, environment.getexternalstoragedirectory()); 
        }else{ 
          toast.maketext(downloadactivity.this, r.string.sdcarderror, 1).show(); 
        } 
         
      } 
    }); 
  } 
  //主线程(ui线程) 
  //业务逻辑正确,但是该程序运行的时候有问题 
  //对于显示控件的界面更新只是由ui线程负责,如果是在非ui线程更新控件的属性值,更新后的显示界面不会反映到屏幕上 
  private void download(final string path, final file savedir) { 
    new thread(new runnable() {      
      @override 
      public void run() { 
        filedownloader loader = new filedownloader(downloadactivity.this, path, savedir, 3); 
        progressbar.setmax(loader.getfilesize());//设置进度条的最大刻度为文件的长度 
        try { 
          loader.download(new downloadprogresslistener() { 
            @override 
            public void ondownloadsize(int size) {//实时获知文件已经下载的数据长度 
              message msg = new message(); 
              msg.what = 1; 
              msg.getdata().putint("size", size); 
              handler.sendmessage(msg);//发送消息 
            } 
          }); 
        } catch (exception e) { 
          handler.obtainmessage(-1).sendtotarget(); 
        } 
      } 
    }).start(); 
  } 
} 

demo下载:multhreaddownload_jb51.rar

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。