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

2016-06-03  本文已影响0人  FredKang

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

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

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

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

// 计算每条线程下载的数据长度,如果整除就平分,不能整除就直接进1
    this.block = (this.fileSize % this.threads.length) == 0 ? this.fileSize
      / this.threads.length
      : this.fileSize / this.threads.length + 1;
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


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

    /* 缓存各线程下载的长度 */  
    private Map<Integer, Integer> data = new ConcurrentHashMap<Integer, Integer>();  
for (Map.Entry<Integer, Integer> 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( )<LEN)
                                   {
                                          while(n.length( )<LEN)
                                          {
                                            n+="/u0000";//空格
                                          }
                                   }
                                  this.name=n;
                                   this.age=a;
                              }
}
class userText1
{
                              public static void main(String args[ ])throws Exception
                              {
                                 Student stu1=new Student("Ada",23);
                                   Student stu2=new Student("Shirlenjklcxvfchfhj",24);
                                  Student stu3=new Student("sunfcvvcxvdfdas",25);
                              RandomAccessFile ra=new RandomAccessFile("student.txt","rw");//写流
                                   ra.write(stu1.name.getBytes( ));//第一的前8个字节,stu1 name属性
                                                                 //将当前对象的name属性的字符串转为8字节数组
                                   ra.write(stu1.age);//(文件的第九个字节)将int类型的age以单字节的保存在文件中,占有一个字节
                                   ra.write(stu2.name.getBytes( ));//第二对象的前8个字节,stu2 name属性
                                   ra.write(stu2.age);
                                   ra.write(stu3.name.getBytes( ));//第三对象的前8个字节,stu3 name属性
                                   ra.write(stu3.age);
                                   ra.close( );
                                   int len=0;
                                  byte buf[]=new byte[8];//长度为8的字节数组.
                              RandomAccessFile raf=new RandomAccessFile("student.txt","r");//读流
                                   //------------------------读对象2属性name,age
                                   raf.skipBytes(9);//跳过9个字节
                                       System.out.println (raf.getFilePointer( ));//指针位置
                                    len=raf.read(buf);//##从文件当中读到的字节放在字节数组中最多只能放8个,并返回读取字节的个数。
                                String str=null;//对象   
                                   str=new String(buf,0,len);//0-8//将字节数组buf[]中的全部内容转为String类型。
                                   System.out.println (str+":"+raf.read( ));
                                   //-------------------------读对象1属性name,age
                                   raf.seek(0);//对指示器进行决对定位
                                       System.out.println (raf.getFilePointer( ));//指针位置
                                   len=raf.read(buf);//读取8个字节
                                   str=new String(buf,0,len);
                                   System.out.println (str+":"+raf.read( ));//age取一个字节
                                  //--------------------------读对象3属性name,ag
                                   raf.skipBytes(9);
                                       System.out.println (raf.getFilePointer( ));//指针位置
                                   len=raf.read(buf);//读取8个字节
                                   str=new String(buf,0,len);
                                   System.out.println (str+":"+raf.read( ));
                                     System.out.println (raf.getFilePointer( ));//指针位置
                                  raf.close( );
                              }

}

多线程分段下载的意义:
网速带宽是一定的,那么为什么多线程下载能加速?(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状态

上一篇下一篇

猜你喜欢

热点阅读