5.31~6.3在線下載服務(wù)器文件捌木,應(yīng)用及通知欄顯示進(jìn)度和斷點多線程分段下載

下載應(yīng)用肋乍,進(jìn)度更新,通知欄顯示

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

多線程分段下載墓造,斷點下載

如果我自己開發(fā)會有哪些疑問:

  • Q:下次下載怎么從上次保存的點下載,不同線程下載的怎么操作同一個文件
    A:skipBytes(long i):從前往后,seek(long p): 從后往前锚烦,隨機(jī)訪問類

  • Q:對一個文件要分幾個線程下載觅闽,每個線程怎么分配數(shù)據(jù)長度
    A:

// 計算每條線程下載的數(shù)據(jù)長度,如果整除就平分,不能整除就直接進(jìn)1
    this.block = (this.fileSize % this.threads.length) == 0 ? this.fileSize
      / this.threads.length
      : this.fileSize / this.threads.length + 1;
  • Q:怎么從服務(wù)器的獲取那個文件的指定位置開始下載
    A:
    block是長度每條線程下載的長度涮俄,downlength是已經(jīng)下載過的長度
int startPos = block * (threadId - 1) + downLength;// 開始位置
int endPos = block * threadId - 1;// 結(jié)束位置
http.setRequestProperty("Range", "bytes=" + startPos + "-"+ endPos);// 設(shè)置獲取實體數(shù)據(jù)的范圍

如果是第一次下載 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

  • 用本地數(shù)據(jù)庫記錄不同線程上次下載的位置孕锄,根據(jù)這個位置去服務(wù)器獲取指定位置的字節(jié)。

              HttpURLConnection http = (HttpURLConnection) downUrl
                      .openConnection();
              http.setConnectTimeout(5 * 1000); // 設(shè)置連接超時
              http.setRequestMethod("GET"); // 設(shè)置請求方法苞尝,這里是“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"); // 瀏覽器所希望的語言種類畸肆,當(dāng)服務(wù)器能夠提供一種以上的語言版本時要用到
              http.setRequestProperty("Referer", downUrl.toString());// 包含一個URL,用戶從該URL代表的頁面出發(fā)訪問當(dāng)前請求的頁面宙址。
              http.setRequestProperty("Charset", "UTF-8"); // 字符集
              int startPos = block * (threadId - 1) + downLength;// 開始位置
              int endPos = block * threadId - 1;// 結(jié)束位置
              http.setRequestProperty("Range", "bytes=" + startPos + "-"
                      + endPos);// 設(shè)置獲取實體數(shù)據(jù)的范圍
              
              // 瀏覽器類型轴脐,如果Servlet返回的內(nèi)容與瀏覽器類型有關(guān)則該值非常有用。
              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"); // 設(shè)置為持久連接
    
              // 得到輸入流
              InputStream inStream = http.getInputStream();
              byte[] buffer = new byte[1024];
              int offset = 0;
              print("Thread " + this.threadId
                      + " start download from position " + startPos);
              // 隨機(jī)訪問文件
              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<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());// 把各條線程已經(jīng)下載的數(shù)據(jù)長度放入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 就是當(dāng)用戶想把請求所得的內(nèi)容存為一個文件的時候提供一個默認(rèn)的文件名
                if ("content-disposition".equals(conn.getHeaderFieldKey(i)  
                        .toLowerCase())) {  

//匹配字段獲取文件名,正則表達(dá)式
                    Matcher m = Pattern.compile(".*filename=(.*)").matcher(  
                            mine.toLowerCase());  

                    if (m.find())  
                        return m.group(1);  
                }  
            }  
            filename = UUID.randomUUID() + ".tmp";// 默認(rèn)取一個文件名  
        }  
        return filename;  
    }  

報頭的Content-disposition
就是當(dāng)用戶想把請求所得的內(nèi)容存為一個文件的時候提供一個默認(rèn)的文件名
就像用電腦的時候,彈窗讓用戶保存東西的時候有個默認(rèn)的名字
如果用于電腦豁辉,服務(wù)端要做的事:
1.當(dāng)代碼里面使用Content-Disposition來確保瀏覽器彈出下載對話框的時候令野。
response.addHeader("Content-Disposition","attachment");一定要確保沒有做過關(guān)于禁止瀏覽器緩存的操作。如下:
response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "No-cache");
response.setDateHeader("Expires", 0);

JAVA正則表達(dá)式 Pattern和Matcher

隨機(jī)訪問文件類RandomAccessFile
我覺得簡單來說就是 分段下載或者斷點下載的分割類徽级,可以看做 節(jié)點流

 // 隨機(jī)訪問文件  
                RandomAccessFile threadfile = new RandomAccessFile(  
                        this.saveFile, "rwd");  
                // 定位到pos位置  
                threadfile.seek(startPos);  

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

白紙習(xí)題

|123456789|123456789|123456789|
                          1條記錄   2條記錄   3條記錄

//練習(xí)隨機(jī)訪問文件類
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個字節(jié)气破,stu1 name屬性
                                                                 //將當(dāng)前對象的name屬性的字符串轉(zhuǎn)為8字節(jié)數(shù)組
                                   ra.write(stu1.age);//(文件的第九個字節(jié))將int類型的age以單字節(jié)的保存在文件中,占有一個字節(jié)
                                   ra.write(stu2.name.getBytes( ));//第二對象的前8個字節(jié)餐抢,stu2 name屬性
                                   ra.write(stu2.age);
                                   ra.write(stu3.name.getBytes( ));//第三對象的前8個字節(jié)现使,stu3 name屬性
                                   ra.write(stu3.age);
                                   ra.close( );
                                   int len=0;
                                  byte buf[]=new byte[8];//長度為8的字節(jié)數(shù)組.
                              RandomAccessFile raf=new RandomAccessFile("student.txt","r");//讀流
                                   //------------------------讀對象2屬性name,age
                                   raf.skipBytes(9);//跳過9個字節(jié)
                                       System.out.println (raf.getFilePointer( ));//指針位置
                                    len=raf.read(buf);//##從文件當(dāng)中讀到的字節(jié)放在字節(jié)數(shù)組中最多只能放8個,并返回讀取字節(jié)的個數(shù)旷痕。
                                String str=null;//對象   
                                   str=new String(buf,0,len);//0-8//將字節(jié)數(shù)組buf[]中的全部內(nèi)容轉(zhuǎn)為String類型碳锈。
                                   System.out.println (str+":"+raf.read( ));
                                   //-------------------------讀對象1屬性name,age
                                   raf.seek(0);//對指示器進(jìn)行決對定位
                                       System.out.println (raf.getFilePointer( ));//指針位置
                                   len=raf.read(buf);//讀取8個字節(jié)
                                   str=new String(buf,0,len);
                                   System.out.println (str+":"+raf.read( ));//age取一個字節(jié)
                                  //--------------------------讀對象3屬性name,ag
                                   raf.skipBytes(9);
                                       System.out.println (raf.getFilePointer( ));//指針位置
                                   len=raf.read(buf);//讀取8個字節(jié)
                                   str=new String(buf,0,len);
                                   System.out.println (str+":"+raf.read( ));
                                     System.out.println (raf.getFilePointer( ));//指針位置
                                  raf.close( );
                              }

}

多線程分段下載的意義:
網(wǎng)速帶寬是一定的,那么為什么多線程下載能加速欺抗?(TCP單流很難利用滿帶寬)
https://www.zhihu.com/question/19914902

F2 AS跳轉(zhuǎn)到錯誤處
Q:RandomAccessFile中mode,rws和rwd的區(qū)別售碳?
A:

先寫這個多線程分段下載
我是這么個步驟,由淺入深
1 我先寫了個下載
2 再寫單線程下載+暫停
3 最后才來寫多線程下載+暫停

瓶頸:
類的方法分類绞呈,比如一個是線程類贸人,一個是下載器類,在線程類處理哪些跟下載器處理哪些歸類不清晰
收獲:
思路很重要

對已 單線程下載+暫停

首次下載 獲取httpurlConnection佃声,connect艺智,獲取輸入流,創(chuàng)建本地文件圾亏,設(shè)置緩沖區(qū)大小十拣, 每次將緩沖的數(shù)據(jù)寫入文件,記錄暫停的位置志鹃,插入數(shù)據(jù)庫

    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{
        //第二次下載是更新數(shù)據(jù)庫的內(nèi)容夭问,向服務(wù)器請求進(jìn)度, 并且拿到已經(jīng)保存的文件曹铃,根據(jù)進(jìn)度缰趋,隨機(jī)訪問文件下載
        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,向服務(wù)器獲取Range參數(shù)
conn.setRequestProperty("Range","byte="+startpos+"-"+apksize);
更新數(shù)據(jù)庫當(dāng)前線程的下載位置

繼續(xù)下載不需要再次調(diào)用connect方法铛只,已經(jīng)是connected狀態(tài)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末埠胖,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子淳玩,更是在濱河造成了極大的恐慌直撤,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蜕着,死亡現(xiàn)場離奇詭異谋竖,居然都是意外死亡红柱,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進(jìn)店門蓖乘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來锤悄,“玉大人,你說我怎么就攤上這事嘉抒×憔郏” “怎么了?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵些侍,是天一觀的道長隶症。 經(jīng)常有香客問我,道長岗宣,這世上最難降的妖魔是什么蚂会? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮耗式,結(jié)果婚禮上胁住,老公的妹妹穿的比我還像新娘。我一直安慰自己刊咳,他們只是感情好彪见,可當(dāng)我...
    茶點故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著芦缰,像睡著了一般企巢。 火紅的嫁衣襯著肌膚如雪枫慷。 梳的紋絲不亂的頭發(fā)上让蕾,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天,我揣著相機(jī)與錄音或听,去河邊找鬼探孝。 笑死,一個胖子當(dāng)著我的面吹牛誉裆,可吹牛的內(nèi)容都是我干的顿颅。 我是一名探鬼主播,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼足丢,長吁一口氣:“原來是場噩夢啊……” “哼粱腻!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起斩跌,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤绍些,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后耀鸦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體柬批,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡啸澡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了氮帐。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嗅虏。...
    茶點故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖上沐,靈堂內(nèi)的尸體忽然破棺而出皮服,到底是詐尸還是另有隱情,我是刑警寧澤参咙,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布冰更,位于F島的核電站,受9級特大地震影響昂勒,放射性物質(zhì)發(fā)生泄漏蜀细。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一戈盈、第九天 我趴在偏房一處隱蔽的房頂上張望奠衔。 院中可真熱鬧,春花似錦塘娶、人聲如沸归斤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽脏里。三九已至,卻和暖如春虹曙,著一層夾襖步出監(jiān)牢的瞬間迫横,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工酝碳, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留矾踱,地道東北人。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓疏哗,卻偏偏與公主長得像呛讲,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子返奉,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,047評論 2 355