1、发送一个含有Rang头的Head请求,如果返回状态码为206,则允许多线程下载
1、使用HttpClient的Head请求获取请求文件的信息
2、发送一个Rang的Head请求判断是否允许多线程下载
3、通过主任务创建多个分段下载线程,分段下载文件,然后用Java的随机读写文件类保存下载的内容
/** * 异步文件下载器,可开启多线程进行下载 * * @author zhihong.lanzh * */ public class FileDownloadAsyncTask extends AsyncTask<Void, Integer, Boolean> { private static String TAG = "FileDownloadAsyncTask"; private int downloadedSize = 0; private int fileSize = 0; private int blockSize, downloadSizeMore; private int threadNum = 5; private String urlStr, fileName; private Context mContext; private ProgressDialog mProgressDialog; private Exception mException = null; private Callback<Boolean> pCallback; private Callback<Exception> pExceptionCallback; private HttpClient httpClient; private boolean acceptRanges = false;// 是否支持多线程下载 public FileDownloadAsyncTask(Context context, String urlStr, int threadNum, String fileName, Callback<Boolean> pCallback) { httpClient = new DefaultHttpClient(); this.urlStr = urlStr; if(threadNum < 1) this.threadNum = 1; else this.threadNum = threadNum; this.fileName = fileName; File file = new File(fileName); if (file.exists()) { file.delete(); } this.mContext = context; this.pCallback = pCallback; this.pExceptionCallback = null; } public FileDownloadAsyncTask(Context context, String urlStr, int threadNum, String fileName, Callback<Boolean> pCallback, Callback<Exception> pExceptionCallback) { this(context, urlStr, threadNum, fileName, pCallback); this.pExceptionCallback = pExceptionCallback; } @Override protected Boolean doInBackground(Void... params) { try { getDownloadFileInfo(); Log.v(TAG, "fileSize:" + fileSize); if (fileSize == -1) { return false; } if (acceptRanges == false) { threadNum = 1; } FileDownloadThread[] fds = new FileDownloadThread[threadNum]; // 计算每个线程要下载的数据量 blockSize = fileSize / threadNum; // 解决整除后百分比计算误差 downloadSizeMore = (fileSize % threadNum); File file = new File(fileName); Log.v(TAG, "doInBackground2"); for (int i = 0; i < threadNum; i++) { // 启动线程,分别下载自己需要下载的部分 FileDownloadThread fdt = new FileDownloadThread(httpClient, urlStr, file, i * blockSize, (i + 1) * blockSize - 1); fdt.setName("Thread" + i); fdt.start(); fds[i] = fdt; } if (threadNum > 1) { // 启动最后一个线程下载最后一部分 FileDownloadThread fdt = new FileDownloadThread(httpClient, urlStr, file, (threadNum - 1) * blockSize, threadNum * blockSize - 1 + downloadSizeMore); fdt.setName("Thread" + (threadNum - 1)); fdt.start(); fds[(threadNum - 1)] = fdt; } boolean finished = false; while (!finished) { finished = true; downloadedSize = 0; for (int i = 0; i < fds.length; i++) { downloadedSize += fds[i].getDownloadSize(); if (!fds[i].isFinished()) { finished = false; } Log.v(TAG, "fds[" + i + "]:" + fds[i].isFinished()); } int progress = (int) (((double) downloadedSize / fileSize) * 100); onProgressUpdate(progress); Log.v(TAG, "downloadedSize:" + downloadedSize); // 休息200ms后再读取下载进度 // Thread.sleep(5); } boolean result = true; for (int i = 0; i < threadNum; i++) { result = result & fds[i].getResult(); } Log.v(TAG, "Result:" + result); return result; } catch (Exception e) { e.printStackTrace(); this.mException = e; return false; } finally { httpClient.getConnectionManager().shutdown(); } } /** * 获取文件下载信息 */ private void getDownloadFileInfo() { try { HttpHead httpHead = new HttpHead(urlStr); HttpResponse response = httpClient.execute(httpHead); if (response.getStatusLine().getStatusCode() != 200) { fileSize = -1; } Log.v(TAG, "response"); // 获取下载文件的总大小 // fileSize = (int) response.getEntity().getContentLength(); Header[] headers = response.getHeaders("Content-Length"); if (headers.length > 0) fileSize = Integer.parseInt(headers[0].getValue()); httpHead.abort(); if (threadNum != 1) { // 判断是否允许多线程下载 httpHead = new HttpHead(urlStr); httpHead.addHeader("Range", "bytes=0-" + (fileSize - 1)); response = httpClient.execute(httpHead); if (response.getStatusLine().getStatusCode() == 206) { acceptRanges = true; } Log.v(TAG, "acceptRanges:" + response.getStatusLine().getStatusCode()); httpHead.abort(); } } catch (ClientProtocolException e) { fileSize = -1; Log.e(TAG, e.getMessage()); // e.printStackTrace(); } catch (IOException e) { fileSize = -1; Log.e(TAG, e.getMessage()); // e.printStackTrace(); } } @Override protected void onPreExecute() { this.mProgressDialog = new ProgressDialog(mContext); this.mProgressDialog.setTitle(R.string.update_dialog_download_title); this.mProgressDialog.setIcon(android.R.drawable.ic_menu_save); this.mProgressDialog.setIndeterminate(false); this.mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); this.mProgressDialog.setMax(100); this.mProgressDialog.show(); super.onPreExecute(); } @Override protected void onPostExecute(Boolean result) { try { if (result != null) this.mProgressDialog.dismiss(); } catch (final Exception e) { Log.e("Error", e.getLocalizedMessage()); /* Nothing. */ } if (this.isCancelled()) { this.mException = new CancelledException(); } if (this.mException == null) { pCallback.onCallback(result); } else { if (pExceptionCallback != null) { pExceptionCallback.onCallback(this.mException); } } super.onPostExecute(result); } @Override protected void onProgressUpdate(Integer... values) { this.mProgressDialog.setProgress(values[0]); super.onProgressUpdate(values); } }
/** * 文件下载线程 * @author zhihong.lanzh * */ public class FileDownloadThread extends Thread { private static final String TAG = "FileDownloadThread"; private static final int BUFFER_SIZE = 1024; private String url; private File file; private int startPosition; private int endPosition; private int curPosition; // 用于标识当前线程是否下载完成 private boolean finished = false; private boolean result = false; private int downloadSize = 0; private Object o = new Object(); private HttpClient httpClient; public FileDownloadThread(HttpClient httpClient , String url, File file, int startPosition, int endPosition) { this.httpClient = httpClient; this.url = url; this.file = file; this.startPosition = startPosition; this.curPosition = startPosition; this.endPosition = endPosition; } @Override public void run() { if(httpClient == null){ this.finished = true; this.result = false; return; } BufferedInputStream bis = null; RandomAccessFile fos = null; byte[] buf = new byte[BUFFER_SIZE]; try { HttpGet httpGet = new HttpGet(url); HttpResponse response = httpClient.execute(httpGet); if(response.getStatusLine().getStatusCode() != 200){ this.finished = true; this.result = false; return; } HttpEntity entity = response.getEntity(); // 设置当前线程下载的起点,终点 httpGet.addHeader("Range", "bytes=" + startPosition + "-" + endPosition); // 使用java中的RandomAccessFile 对文件进行随机读写操作 fos = new RandomAccessFile(file, "rw"); // 设置开始写文件的位置 fos.seek(startPosition); bis = new BufferedInputStream(entity.getContent()); // 开始循环以流的形式读写文件 while (curPosition < endPosition) { int len = bis.read(buf, 0, BUFFER_SIZE); if (len == -1) { break; } fos.write(buf, 0, len); curPosition = curPosition + len; synchronized (o) { if (curPosition > endPosition) { downloadSize += len - (curPosition - endPosition) + 1; } else { downloadSize += len; } } // Log.v(TAG, "curPosition:" + curPosition + ",endPosition:" + endPosition + ",downloadSize:" + downloadSize); // sleep(100); } // 下载完成设为true this.finished = true; this.result = true; bis.close(); fos.close(); httpGet.abort(); } catch (Exception e) { result = false; e.printStackTrace(); Log.v(TAG, e.getMessage()); } } public boolean isFinished() { return finished; } public int getDownloadSize() { synchronized (o) { return downloadSize; } } public boolean getResult() { return result; } public void setResult(boolean result) { this.result = result; } }
http://zywang.iteye.com/blog/916489
http://blog.csdn.net/goodname008/article/details/568668