下面的类是文件下载类,支持文件的多线程断点续传,使用该类的即可安全、高效的下载任何类型的二进制文件:

import java.io.File;

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.RandomAccessFile;

import java.net.HttpURLConnection;

import java.net.URL;

import java.util.LinkedHashMap;

import java.util.Map;

import java.util.Properties;

import java.util.UUID;

import java.util.concurrent.ConcurrentHashMap;

import java.util.regex.Matcher;

import java.util.regex.Pattern;


import cn.itcast.service.FileService;


import android.content.Context;

import android.util.Log;

/**

* 文件下载器

*/

public class FileDownloader {

private Context context;

private FileService fileService;


private static final String TAG = "FileDownloader";

/* 已下载文件大小 */

private int downloadSize = 0;

/* 原始文件大小 */

private int fileSize = 0;

/* 线程数 */

private DownloadThread[] threads;

/* 下载路径 */

private URL url;

/* 本地保存文件 */

private File saveFile;

/* 下载记录文件 */

private File logFile;

/* 缓存各线程最后下载的位置*/

private Map data = new ConcurrentHashMap();

/* 每条线程下载的大小 */

private int block;

private String downloadUrl;//下载路径

/**

* 获取线程数

*/

public int getThreadSize() {

return threads.length;

}

/**

* 获取文件大小

* @return

*/

public int getFileSize() {

return fileSize;

}

/**

* 累计已下载大小

* @param size

*/

protected synchronized void append(int size) {

downloadSize += size;

}

/**

* 更新指定线程最后下载的位置

* @param threadId 线程id

* @param pos 最后下载的位置

*/

protected void update(int threadId, int pos) {

this.data.put(threadId, pos);

}

/**

* 保存记录文件

*/

protected synchronized void saveLogFile() {

this.fileService.update(this.downloadUrl, this.data);

}

/**

* 构建文件下载器

* @param downloadUrl 下载路径

* @param fileSaveDir 文件保存目录

* @param threadNum 下载线程数

*/

public FileDownloader(Context context, String downloadUrl, File fileSaveDir, int threadNum) {

try {

this.context = context;

this.downloadUrl = downloadUrl;

fileService = new FileService(context);

this.url = new URL(downloadUrl);

if(!fileSaveDir.exists()) fileSaveDir.mkdirs();

this.threads = new DownloadThread[threadNum]; 

HttpURLConnection conn = (HttpURLConnection) url.openConnection();

conn.setConnectTimeout(6*1000);

conn.setRequestMethod("GET");

conn.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, */*");

conn.setRequestProperty("Accept-Language", "zh-CN");

conn.setRequestProperty("Referer", downloadUrl); 

conn.setRequestProperty("Charset", "UTF-8");

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)");

conn.setRequestProperty("Connection", "Keep-Alive");

conn.connect();

printResponseHeader(conn);

if (conn.getResponseCode()==200) {

this.fileSize = conn.getContentLength();//根据响应获取文件大小

if (this.fileSize <= 0) throw new RuntimeException("1无法获知文件大小 ");


String filename = getFileName(conn);

this.saveFile = new File(fileSaveDir, filename);/* 保存文件 */

Map logdata = fileService.getData(downloadUrl);

if(logdata.size()>0){

for(Map.Entry entry : logdata.entrySet())

data.put(entry.getKey(), entry.getValue()+1);

}

this.block = this.fileSize / this.threads.length + 1;

if(this.data.size()==this.threads.length){

for (int i = 0; i < this.threads.length; i++) {

this.downloadSize += this.data.get(i+1)-(this.block * i);

}

print("已经下载的长度"+ this.downloadSize);

} 

}else{

throw new RuntimeException("2服务器响应错误 ");

}

} catch (Exception e) {

print(e.toString());

throw new RuntimeException("3连接不到下载路径 ");

}

}

/**

* 获取文件名

*/

private String getFileName(HttpURLConnection conn) {

String filename = this.url.toString().substring(this.url.toString().lastIndexOf('/') + 1);

if(filename==null || "".equals(filename.trim())){//如果获取不到文件名称

for (int i = 0;; i++) {

String mine = conn.getHeaderField(i);

if (mine == null) break;

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;

}


/**

* 开始下载文件

* @param listener 监听下载数量的变化,如果不需要了解实时下载的数量,可以设置为null

* @return 已下载文件大小

* @throws Exception

*/

public int download(DownloadProgressListener listener) throws Exception{

try { 

if(this.data.size() != this.threads.length){

this.data.clear();

for (int i = 0; i < this.threads.length; i++) {

this.data.put(i+1, this.block * i);

}

}

for (int i = 0; i < this.threads.length; i++) {

int downLength = this.data.get(i+1) - (this.block * i);

if(downLength < this.block && this.data.get(i+1)0) randOut.setLength(this.fileSize); 

randOut.seek(this.data.get(i+1));

this.threads[i] = new DownloadThread(this, this.url, randOut, this.block, this.data.get(i+1), i+1);

this.threads[i].setPriority(7);

this.threads[i].start();

}else{

this.threads[i] = null;

}

}

this.fileService.save(this.downloadUrl, this.data);

boolean notFinish = true;//下载未完成

while (notFinish) {// 循环判断是否下载完毕

Thread.sleep(900);

notFinish = false;//假定下载完成

for (int i = 0; i < this.threads.length; i++){

if (this.threads[i] != null && !this.threads[i].isFinish()) {

notFinish = true;//下载没有完成

if(this.threads[i].getDownLength() == -1){//如果下载失败,再重新下载

RandomAccessFile randOut = new RandomAccessFile(this.saveFile, "rw");

randOut.seek(this.data.get(i+1));

this.threads[i] = new DownloadThread(this, this.url, randOut, this.block, this.data.get(i+1), i+1);

this.threads[i].setPriority(7);

this.threads[i].start();

}

}

} 

if(listener!=null) listener.onDownloadSize(this.downloadSize);

}

fileService.delete(this.downloadUrl);

} catch (Exception e) {

print(e.toString());

throw new Exception("下载失败");

}

return this.downloadSize;

}

/**

* 获取Http响应头字段

* @param http

* @return

*

你可能感兴趣的:(android)