5.31~6.3在线下载服务器文件,应用及通知栏显示进度和断点多线程分段下载

  • 获取网络文件大小
    关于inputStream.available()方法获取下载文件的总大小
    inputStream.available()获取输入流的大小,但是只有当流不阻塞的时候才能用。网路不稳定,read()方法是阻塞的。要通过获取
    要通过httpconn.getContentLength();即
    getHeaderFieldInt("Content-Length", -1);从解析头即Content-length获取大小
    Closeable之带资源的try语句 (jdk 1.7引入)
    探究java IO之AutoCloseable,Closeable和Flushable接口

下载应用,进度更新,通知栏显示

  • 下载后软件安装,安装好后打开软件
安装软件
if (beginDownload.getText().equals("安装")){
            Intent intent=new Intent(Intent.ACTION_VIEW);
            intent.setDataAndType(Uri.fromFile(file),"application/vnd.android.package-archive");//Type有哪些
            startActivity(intent);
        }

多线程分段下载,断点下载

如果我自己开发会有哪些疑问:

  • Q:下次下载怎么从上次保存的点下载,不同线程下载的怎么操作同一个文件
    A:skipBytes(long i):从前往后,seek(long p): 从后往前,随机访问类

  • Q:对一个文件要分几个线程下载,每个线程怎么分配数据长度
    A:

// 计算每条线程下载的数据长度,如果整除就平分,不能整除就直接进1
    this.block = (this.fileSize % this.threads.length) == 0 ? this.fileSize
      / this.threads.length
      : this.fileSize / this.threads.length + 1;
  • Q:怎么从服务器的获取那个文件的指定位置开始下载
    A:
    block是长度每条线程下载的长度,downlength是已经下载过的长度
int startPos = block * (threadId - 1) + downLength;// 开始位置
int endPos = block * threadId - 1;// 结束位置
http.setRequestProperty("Range", "bytes=" + startPos + "-"+ endPos);// 设置获取实体数据的范围

如果是第一次下载 downlength为0
比如19分两条线程 block是10, 1,0~9 2,10~19
或者18分三条线程 block是6,1,0~5 2,611,1217

参考帖子:
http://blog.csdn.net/wwj_748/article/details/20146869

  • 用本地数据库记录不同线程上次下载的位置,根据这个位置去服务器获取指定位置的字节。

              HttpURLConnection http = (HttpURLConnection) downUrl
                      .openConnection();
              http.setConnectTimeout(5 * 1000); // 设置连接超时
              http.setRequestMethod("GET"); // 设置请求方法,这里是“GET”
              // 浏览器可接受的MIME类型
              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());// 包含一个URL,用户从该URL代表的页面出发访问当前请求的页面。
              http.setRequestProperty("Charset", "UTF-8"); // 字符集
              int startPos = block * (threadId - 1) + downLength;// 开始位置
              int endPos = block * threadId - 1;// 结束位置
              http.setRequestProperty("Range", "bytes=" + startPos + "-"
                      + endPos);// 设置获取实体数据的范围
              
              // 浏览器类型,如果Servlet返回的内容与浏览器类型有关则该值非常有用。
              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");
              // 定位到pos位置
              threadfile.seek(startPos);
              while (!downloader.getExit()
                      && (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;
    
  • 缓存各线程下载的长度


   Map logdata = fileService  
                        .getData(downloadUrl);// 获取下载记录           

    /* 缓存各线程下载的长度 */  
    private Map data = new ConcurrentHashMap();  
for (Map.Entry entry : logdata.entrySet())  //entryS et()获取对象
   data.put(entry.getKey(), entry.getValue());// 把各条线程已经下载的数据长度放入data中  

获取文件名,获取连接最后一个“/”后面的或者从HeaderField获取

 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;  

// content-disposition 就是当用户想把请求所得的内容存为一个文件的时候提供一个默认的文件名
                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;  
    }  

报头的Content-disposition
就是当用户想把请求所得的内容存为一个文件的时候提供一个默认的文件名
就像用电脑的时候,弹窗让用户保存东西的时候有个默认的名字
如果用于电脑,服务端要做的事:
1.当代码里面使用Content-Disposition来确保浏览器弹出下载对话框的时候。
response.addHeader("Content-Disposition","attachment");一定要确保没有做过关于禁止浏览器缓存的操作。如下:
response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "No-cache");
response.setDateHeader("Expires", 0);

JAVA正则表达式 Pattern和Matcher

随机访问文件类RandomAccessFile
我觉得简单来说就是 分段下载或者断点下载的分割类,可以看做 节点流

 // 随机访问文件  
                RandomAccessFile threadfile = new RandomAccessFile(  
                        this.saveFile, "rwd");  
                // 定位到pos位置  
                threadfile.seek(startPos);  

有点切割字符串的方式切割文件打印字符串

白纸习题

|123456789|123456789|123456789|
                          1条记录   2条记录   3条记录

//练习随机访问文件类
import java.io.*;
class Student
{
                              String name="aaaaaaa";
                              int age=0;
                              public static final int LEN=8;
                              public Student (String n,int a)
                              {
                                   if(n.length( )>LEN)
                                   {
                                          n=n.substring(0,LEN);
                                   }
                                  if(n.length( )

多线程分段下载的意义:
网速带宽是一定的,那么为什么多线程下载能加速?(TCP单流很难利用满带宽)
https://www.zhihu.com/question/19914902

F2 AS跳转到错误处
Q:RandomAccessFile中mode,rws和rwd的区别?
A:

先写这个多线程分段下载
我是这么个步骤,由浅入深
1 我先写了个下载
2 再写单线程下载+暂停
3 最后才来写多线程下载+暂停

瓶颈:
类的方法分类,比如一个是线程类,一个是下载器类,在线程类处理哪些跟下载器处理哪些归类不清晰
收获:
思路很重要

对已 单线程下载+暂停

首次下载 获取httpurlConnection,connect,获取输入流,创建本地文件,设置缓冲区大小, 每次将缓冲的数据写入文件,记录暂停的位置,插入数据库

    SqlCreater sqlCreater = new SqlCreater(GetNetworkSize.this);
    DbOperator dbOperator = new DbOperator(sqlCreater.getWritableDatabase());
    HttpURLConnection urlConnection=Network.urlConnection(apkpath);
    if (isFirst){
        isFirst=false;
        try {
            InputStream in=urlConnection.getInputStream();
            apksize=urlConnection.getContentLength()+"";
            file=new File(apkpath);
            FileOutputStream fo=new FileOutputStream(file);
            byte[]b=new byte[1024];
            int line=0;
            while ((line=in.read())!=-1){
                fo.write(b,0,line);
                currentApksize+=line;
                //第一次下载是添加
                dbOperator.add(1,currentApksize);


                p = (float) currentApksize / (float) Integer.valueOf(apksize) * 100;
                pro = (int) p;
                handler.sendEmptyMessage(SINGLEDOWN);
            }
            fo.close();
            in.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    else{
        //第二次下载是更新数据库的内容,向服务器请求进度, 并且拿到已经保存的文件,根据进度,随机访问文件下载
        int startpos=dbOperator.getCurrentApkSize("multidownload");
        try {
            URL url=new URL(apkpath);
            HttpURLConnection conn= (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setReadTimeout(5000);
            conn.setRequestProperty("Range","byte="+startpos+"-"+apksize);
            InputStream is=conn.getInputStream();
            RandomAccessFile continuefile=new RandomAccessFile(file,"rwd");
            byte[]b=new byte[1024];
            int line=0;
            while ((line=is.read())!=-1){
                continuefile.write(b,0,line);
                currentApksize+=line;
                handler.sendEmptyMessage(SINGLEDOWN);
            }
            is.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        dbOperator.update(1,currentApksize);

    }

}

非首次下载 获取上次的位置startpos,向服务器获取Range参数
conn.setRequestProperty("Range","byte="+startpos+"-"+apksize);
更新数据库当前线程的下载位置

继续下载不需要再次调用connect方法,已经是connected状态

你可能感兴趣的:(5.31~6.3在线下载服务器文件,应用及通知栏显示进度和断点多线程分段下载)