天天看點

斷點下載下傳

很多時候我們需要在Android裝置上下載下傳遠端伺服器上的檔案進安裝,前兩天晚上我看到一個視訊,寫了兩個晚上,大概了解一下。   直接通過Android提供的Http類通路遠端伺服器,這裡AndroidHttpClient是SDK 2.2中新出的方法,

下載下傳斷點檔案。

下面讓我們看一下圖先。

  1. 讓我們看一下代碼的實作方法。 
  2. package com.smart.db; 
  3. import java.util.HashMap; 
  4. import java.util.Map; 
  5. import android.content.Context; 
  6. import android.database.Cursor; 
  7. import android.database.sqlite.SQLiteDatabase; 
  8. /** 
  9. * 業務bean 
  10. */ 
  11. public class FileService { 
  12. private DBOpenHelper openHelper; 
  13. public FileService(Context context) { 
  14.   openHelper = new DBOpenHelper(context); 
  15. /** 
  16.   * 擷取每條線程已經下載下傳的檔案長度 
  17.   * @param path 
  18.   * @return 
  19.   */ 
  20. public Map<Integer, Integer> getData(String path){ 
  21.   SQLiteDatabase db = openHelper.getReadableDatabase(); 
  22.   Cursor cursor = db.rawQuery("select threadid, downlength from SmartFileDownlog where downpath=?", new String[]{path}); 
  23.   Map<Integer, Integer> data = new HashMap<Integer, Integer>(); 
  24.   while(cursor.moveToNext()){ 
  25.    data.put(cursor.getInt(0), cursor.getInt(1)); 
  26.   } 
  27.   cursor.close(); 
  28.   db.close(); 
  29.   return data; 
  30. /** 
  31.   * 儲存每條線程已經下載下傳的檔案長度 
  32.   * @param path 
  33.   * @param map 
  34.   */ 
  35. public void save(String path,  Map<Integer, Integer> map){//int threadid, int position 
  36.   SQLiteDatabase db = openHelper.getWritableDatabase(); 
  37.   db.beginTransaction(); 
  38.   try{ 
  39.    for(Map.Entry<Integer, Integer> entry : map.entrySet()){ 
  40.     db.execSQL("insert into SmartFileDownlog(downpath, threadid, downlength) values(?,?,?)", 
  41.       new Object[]{path, entry.getKey(), entry.getValue()}); 
  42.    } 
  43.    db.setTransactionSuccessful(); 
  44.   }finally{ 
  45.    db.endTransaction(); 
  46.   } 
  47.   db.close(); 
  48. /** 
  49.   * 實時更新每條線程已經下載下傳的檔案長度 
  50.   * @param path 
  51.   * @param map 
  52.   */ 
  53. public void update(String path, Map<Integer, Integer> map){ 
  54.   SQLiteDatabase db = openHelper.getWritableDatabase(); 
  55.   db.beginTransaction(); 
  56.   try{ 
  57.    for(Map.Entry<Integer, Integer> entry : map.entrySet()){ 
  58.     db.execSQL("update SmartFileDownlog set downlength=? where downpath=? and threadid=?", 
  59.       new Object[]{entry.getValue(), path, entry.getKey()}); 
  60.    } 
  61.    db.setTransactionSuccessful(); 
  62.   }finally{ 
  63.    db.endTransaction(); 
  64.   } 
  65.   db.close(); 
  66. /** 
  67.   * 當檔案下載下傳完成後,删除對應的下載下傳記錄 
  68.   * @param path 
  69.   */ 
  70. public void delete(String path){ 
  71.   SQLiteDatabase db = openHelper.getWritableDatabase(); 
  72.   db.execSQL("delete from SmartFileDownlog where downpath=?", new Object[]{path}); 
  73.   db.close(); 
  74. package com.smart.impl; 
  75. import java.io.File; 
  76. import java.io.RandomAccessFile; 
  77. import java.net.HttpURLConnection; 
  78. import java.net.URL; 
  79. import java.util.LinkedHashMap; 
  80. import java.util.Map; 
  81. import java.util.UUID; 
  82. import java.util.concurrent.ConcurrentHashMap; 
  83. import java.util.regex.Matcher; 
  84. import java.util.regex.Pattern; 
  85. import android.content.Context; 
  86. import android.util.Log; 
  87. import com.smart.db.FileService; 
  88. /** 
  89. * 檔案下載下傳器 
  90. * @author [email protected] 
  91. */ 
  92. public class SmartFileDownloader { 
  93. private static final String TAG = "SmartFileDownloader"; 
  94. private Context context; 
  95. private FileService fileService; 
  96. /* 已下載下傳檔案長度 */ 
  97. private int downloadSize = 0; 
  98. /* 原始檔案長度 */ 
  99. private int fileSize = 0; 
  100. /* 線程數 */ 
  101. private SmartDownloadThread[] threads; 
  102. /* 本地儲存檔案 */ 
  103. private File saveFile; 
  104. /* 緩存各線程下載下傳的長度*/ 
  105. private Map<Integer, Integer> data = new ConcurrentHashMap<Integer, Integer>(); 
  106. /* 每條線程下載下傳的長度 */ 
  107. private int block; 
  108. /* 下載下傳路徑  */ 
  109. private String downloadUrl; 
  110. /** 
  111.   * 擷取線程數 
  112.   */ 
  113. public int getThreadSize() { 
  114.   return threads.length; 
  115. /** 
  116.   * 擷取檔案大小 
  117.   * @return 
  118.   */ 
  119. public int getFileSize() { 
  120.   return fileSize; 
  121. /** 
  122.   * 累計已下載下傳大小 
  123.   * @param size 
  124.   */ 
  125. protected synchronized void append(int size) { 
  126.   downloadSize += size; 
  127. /** 
  128.   * 更新指定線程最後下載下傳的位置 
  129.   * @param threadId 線程id 
  130.   * @param pos 最後下載下傳的位置 
  131.   */ 
  132. protected void update(int threadId, int pos) { 
  133.   this.data.put(threadId, pos); 
  134. /** 
  135.   * 儲存記錄檔案 
  136.   */ 
  137. protected synchronized void saveLogFile() { 
  138.   this.fileService.update(this.downloadUrl, this.data); 
  139. /** 
  140.   * 建構檔案下載下傳器 
  141.   * @param downloadUrl 下載下傳路徑 
  142.   * @param fileSaveDir 檔案儲存目錄 
  143.   * @param threadNum 下載下傳線程數 
  144.   */ 
  145. public SmartFileDownloader(Context context, String downloadUrl, File fileSaveDir, int threadNum) { 
  146.   try { 
  147.    this.context = context; 
  148.    this.downloadUrl = downloadUrl; 
  149.    fileService = new FileService(this.context); 
  150.    URL url = new URL(this.downloadUrl); 
  151.    if(!fileSaveDir.exists()) fileSaveDir.mkdirs(); 
  152.    this.threads = new SmartDownloadThread[threadNum];      
  153.    HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 
  154.    conn.setConnectTimeout(5*1000); 
  155.    conn.setRequestMethod("GET"); 
  156.    conn.setRequestProperty("Accept", "p_w_picpath/gif, p_w_picpath/jpeg, p_w_picpath/pjpeg, p_w_picpath/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, */*"); 
  157.    conn.setRequestProperty("Accept-Language", "zh-CN"); 
  158.    conn.setRequestProperty("Referer", downloadUrl); 
  159.    conn.setRequestProperty("Charset", "UTF-8"); 
  160.    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)"); 
  161.    conn.setRequestProperty("Connection", "Keep-Alive"); 
  162.    conn.connect(); 
  163.    printResponseHeader(conn); 
  164.    if (conn.getResponseCode()==200) { 
  165.     this.fileSize = conn.getContentLength();//根據響應擷取檔案大小 
  166.     if (this.fileSize <= 0) throw new RuntimeException("Unkown file size "); 
  167.     String filename = getFileName(conn); 
  168.     this.saveFile = new File(fileSaveDir, filename);/* 儲存檔案 */ 
  169.     Map<Integer, Integer> logdata = fileService.getData(downloadUrl); 
  170.     if(logdata.size()>0){ 
  171.      for(Map.Entry<Integer, Integer> entry : logdata.entrySet()) 
  172.       data.put(entry.getKey(), entry.getValue()); 
  173.     } 
  174.     this.block = (this.fileSize % this.threads.length)==0? this.fileSize / this.threads.length : this.fileSize / this.threads.length + 1; 
  175.     if(this.data.size()==this.threads.length){ 
  176.      for (int i = 0; i < this.threads.length; i++) { 
  177.       this.downloadSize += this.data.get(i+1); 
  178.      } 
  179.      print("已經下載下傳的長度"+ this.downloadSize); 
  180.     }    
  181.    }else{ 
  182.     throw new RuntimeException("server no response "); 
  183.    } 
  184.   } catch (Exception e) { 
  185.    print(e.toString()); 
  186.    throw new RuntimeException("don't connection this url"); 
  187.   } 
  188. /** 
  189.   * 擷取檔案名 
  190.   */ 
  191. private String getFileName(HttpURLConnection conn) { 
  192.   String filename = this.downloadUrl.substring(this.downloadUrl.lastIndexOf('/') + 1); 
  193.   if(filename==null || "".equals(filename.trim())){//如果擷取不到檔案名稱 
  194.    for (int i = 0;; i++) { 
  195.     String mine = conn.getHeaderField(i); 
  196.     if (mine == null) break; 
  197.     if("content-disposition".equals(conn.getHeaderFieldKey(i).toLowerCase())){ 
  198.      Matcher m = Pattern.compile(".*filename=(.*)").matcher(mine.toLowerCase()); 
  199.      if(m.find()) return m.group(1); 
  200.     } 
  201.    } 
  202.    filename = UUID.randomUUID()+ ".tmp";//預設取一個檔案名 
  203.   } 
  204.   return filename; 
  205. /** 
  206.   *  開始下載下傳檔案 
  207.   * @param listener 監聽下載下傳數量的變化,如果不需要了解實時下載下傳的數量,可以設定為null 
  208.   * @return 已下載下傳檔案大小 
  209.   * @throws Exception 
  210.   */ 
  211. public int download(SmartDownloadProgressListener listener) throws Exception{ 
  212.   try { 
  213.    RandomAccessFile randOut = new RandomAccessFile(this.saveFile, "rw"); 
  214.    if(this.fileSize>0) randOut.setLength(this.fileSize); 
  215.    randOut.close(); 
  216.    URL url = new URL(this.downloadUrl); 
  217.    if(this.data.size() != this.threads.length){ 
  218.     this.data.clear();//清除資料 
  219.     for (int i = 0; i < this.threads.length; i++) { 
  220.      this.data.put(i+1, 0); 
  221.     } 
  222.    } 
  223.    for (int i = 0; i < this.threads.length; i++) { 
  224.     int downLength = this.data.get(i+1); 
  225.     if(downLength < this.block && this.downloadSize<this.fileSize){ //該線程未完成下載下傳時,繼續下載下傳       
  226.      this.threads = new SmartDownloadThread(this, url, this.saveFile, this.block, this.data.get(i+1), i+1); 
  227.      this.threads.setPriority(7); 
  228.      this.threads.start(); 
  229.     }else{ 
  230.      this.threads = null; 
  231.     } 
  232.    } 
  233.    this.fileService.save(this.downloadUrl, this.data); 
  234.    boolean notFinish = true;//下載下傳未完成 
  235.    while (notFinish) {// 循環判斷是否下載下傳完畢 
  236.     Thread.sleep(900); 
  237.     notFinish = false;//假定下載下傳完成 
  238.     for (int i = 0; i < this.threads.length; i++){ 
  239.      if (this.threads != null && !this.threads.isFinish()) { 
  240.       notFinish = true;//下載下傳沒有完成 
  241.       if(this.threads.getDownLength() == -1){//如果下載下傳失敗,再重新下載下傳 
  242.        this.threads = new SmartDownloadThread(this, url, this.saveFile, this.block, this.data.get(i+1), i+1); 
  243.        this.threads.setPriority(7); 
  244.        this.threads.start(); 
  245.       } 
  246.      } 
  247.     }    
  248.     if(listener!=null) listener.onDownloadSize(this.downloadSize); 
  249.    } 
  250.    fileService.delete(this.downloadUrl); 
  251.   } catch (Exception e) { 
  252.    print(e.toString()); 
  253.    throw new Exception("file download fail"); 
  254.   } 
  255.   return this.downloadSize; 
  256. /** 
  257.   * 擷取Http響應頭字段 
  258.   * @param http 
  259.   * @return 
  260.   */ 
  261. public static Map<String, String> getHttpResponseHeader(HttpURLConnection http) { 
  262.   Map<String, String> header = new LinkedHashMap<String, String>(); 
  263.   for (int i = 0;; i++) { 
  264.    String mine = http.getHeaderField(i); 
  265.    if (mine == null) break; 
  266.    header.put(http.getHeaderFieldKey(i), mine); 
  267.   } 
  268.   return header; 
  269. /** 
  270.   * 列印Http頭字段 
  271.   * @param http 
  272.   */ 
  273. public static void printResponseHeader(HttpURLConnection http){ 
  274.   Map<String, String> header = getHttpResponseHeader(http); 
  275.   for(Map.Entry<String, String> entry : header.entrySet()){ 
  276.    String key = entry.getKey()!=null ? entry.getKey()+ ":" : ""; 
  277.    print(key+ entry.getValue()); 
  278.   } 
  279. //列印日志 
  280. private static void print(String msg){ 
  281.   Log.i(TAG, msg); 
  282. package com.smart.impl; 
  283. import java.io.File; 
  284. import java.io.InputStream; 
  285. import java.io.RandomAccessFile; 
  286. import java.net.HttpURLConnection; 
  287. import java.net.URL; 
  288. import android.util.Log; 
  289. public class SmartDownloadThread extends Thread { 
  290. private static final String TAG = "SmartDownloadThread"; 
  291. private File saveFile; 
  292. private URL downUrl; 
  293. private int block; 
  294. /* *下載下傳開始位置  */ 
  295. private int threadId = -1; 
  296. private int downLength; 
  297. private boolean finish = false; 
  298. private SmartFileDownloader downloader; 
  299. public SmartDownloadThread(SmartFileDownloader downloader, URL downUrl, File saveFile, int block, int downLength, int threadId) { 
  300.   this.downUrl = downUrl; 
  301.   this.saveFile = saveFile; 
  302.   this.block = block; 
  303.   this.downloader = downloader; 
  304.   this.threadId = threadId; 
  305.   this.downLength = downLength; 
  306. @Override 
  307. public void run() { 
  308.   if(downLength < block){//未下載下傳完成 
  309.    try { 
  310.     HttpURLConnection http = (HttpURLConnection) downUrl.openConnection(); 
  311.     http.setConnectTimeout(5 * 1000); 
  312.     http.setRequestMethod("GET"); 
  313.     http.setRequestProperty("Accept", "p_w_picpath/gif, p_w_picpath/jpeg, p_w_picpath/pjpeg, p_w_picpath/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, */*"); 
  314.     http.setRequestProperty("Accept-Language", "zh-CN"); 
  315.     http.setRequestProperty("Referer", downUrl.toString()); 
  316.     http.setRequestProperty("Charset", "UTF-8"); 
  317.     int startPos = block * (threadId - 1) + downLength;//開始位置 
  318.     int endPos = block * threadId -1;//結束位置 
  319.     http.setRequestProperty("Range", "bytes=" + startPos + "-"+ endPos);//設定擷取實體資料的範圍 
  320.     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)"); 
  321.     http.setRequestProperty("Connection", "Keep-Alive"); 
  322.     InputStream inStream = http.getInputStream(); 
  323.     byte[] buffer = new byte[1024]; 
  324.     int offset = 0; 
  325.     print("Thread " + this.threadId + " start download from position "+ startPos); 
  326.     RandomAccessFile threadfile = new RandomAccessFile(this.saveFile, "rwd"); 
  327.     threadfile.seek(startPos); 
  328.     while ((offset = inStream.read(buffer, 0, 1024)) != -1) { 
  329.      threadfile.write(buffer, 0, offset); 
  330.      downLength += offset; 
  331.      downloader.update(this.threadId, downLength); 
  332.      downloader.saveLogFile(); 
  333.      downloader.append(offset); 
  334.     } 
  335.     threadfile.close(); 
  336.     inStream.close();    
  337.     print("Thread " + this.threadId + " download finish"); 
  338.     this.finish = true; 
  339.    } catch (Exception e) { 
  340.     this.downLength = -1; 
  341.     print("Thread "+ this.threadId+ ":"+ e); 
  342.    } 
  343.   } 
  344. private static void print(String msg){ 
  345.   Log.i(TAG, msg); 
  346. /** 
  347.   * 下載下傳是否完成 
  348.   * @return 
  349.   */ 
  350. public boolean isFinish() { 
  351.   return finish; 
  352. /** 
  353.   * 已經下載下傳的内容大小 
  354.   * @return 如果傳回值為-1,代表下載下傳失敗 
  355.   */ 
  356. public long getDownLength() { 
  357.   return downLength; 
  358. package com.smart.activoty.download; 
  359. import java.io.File; 
  360. import android.app.Activity; 
  361. import android.os.Bundle; 
  362. import android.os.Environment; 
  363. import android.os.Handler; 
  364. import android.os.Message; 
  365. import android.view.View; 
  366. import android.widget.Button; 
  367. import android.widget.EditText; 
  368. import android.widget.ProgressBar; 
  369. import android.widget.TextView; 
  370. import android.widget.Toast; 
  371. import com.smart.impl.SmartDownloadProgressListener; 
  372. import com.smart.impl.SmartFileDownloader; 
  373. public class SmartDownloadActivity extends Activity { 
  374.     private ProgressBar downloadbar; 
  375.     private EditText pathText; 
  376.     private TextView resultView; 
  377.     private Handler handler = new Handler(){ 
  378.   @Override//資訊 
  379.   public void handleMessage(Message msg) { 
  380.    switch (msg.what) { 
  381.    case 1: 
  382.     int size = msg.getData().getInt("size"); 
  383.     downloadbar.setProgress(size); 
  384.     float result = (float)downloadbar.getProgress()/ (float)downloadbar.getMax(); 
  385.     int p = (int)(result*100); 
  386.     resultView.setText(p+"%"); 
  387.     if(downloadbar.getProgress()==downloadbar.getMax()) 
  388.      Toast.makeText(SmartDownloadActivity.this, R.string.success, 1).show(); 
  389.     break; 
  390.    case -1: 
  391.     Toast.makeText(SmartDownloadActivity.this, R.string.error, 1).show(); 
  392.     break; 
  393.    } 
  394.   }      
  395.     }; 
  396.     @Override 
  397.     public void onCreate(Bundle savedInstanceState) { 
  398.         super.onCreate(savedInstanceState); 
  399.         setContentView(R.layout.main); 
  400.         Button button = (Button)this.findViewById(R.id.button); 
  401.         downloadbar = (ProgressBar)this.findViewById(R.id.downloadbar); 
  402.         pathText = (EditText)this.findViewById(R.id.path); 
  403.         resultView = (TextView)this.findViewById(R.id.result); 
  404.         button.setOnClickListener(new View.OnClickListener() {    
  405.    @Override 
  406.    public void onClick(View v) { 
  407.     String path = pathText.getText().toString(); 
  408.     if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){ 
  409.      File dir = Environment.getExternalStorageDirectory();//檔案儲存目錄 
  410.      download(path, dir); 
  411.     }else{ 
  412.      Toast.makeText(SmartDownloadActivity.this, R.string.sdcarderror, 1).show(); 
  413.     } 
  414.    } 
  415.   }); 
  416.     } 
  417.     //對于UI控件的更新隻能由主線程(UI線程)負責,如果在非UI線程更新UI控件,更新的結果不會反映在螢幕上,某些控件還會出錯 
  418.     private void download(final String path, final File dir){ 
  419.      new Thread(new Runnable() { 
  420.    @Override 
  421.    public void run() { 
  422.     try { 
  423.      SmartFileDownloader loader = new SmartFileDownloader(SmartDownloadActivity.this, path, dir, 3); 
  424.      int length = loader.getFileSize();//擷取檔案的長度 
  425.      downloadbar.setMax(length); 
  426.      loader.download(new SmartDownloadProgressListener(){ 
  427.       @Override 
  428.       public void onDownloadSize(int size) {//可以實時得到檔案下載下傳的長度 
  429.        Message msg = new Message(); 
  430.        msg.what = 1; 
  431.        msg.getData().putInt("size", size);       
  432.        handler.sendMessage(msg); 
  433.       }}); 
  434.     } catch (Exception e) { 
  435.      Message msg = new Message();//資訊提示 
  436.      msg.what = -1; 
  437.      msg.getData().putString("error", "下載下傳失敗");//如果下載下傳錯誤,顯示提示失敗! 
  438.      handler.sendMessage(msg); 
  439.     } 
  440.    } 
  441.   }).start();//開始 
  442.     } 
上一篇: 斷點調試
下一篇: 條件斷點