让我们看一下代码的实现方法。
packagecom.smart.db;
importjava.util.HashMap;
importjava.util.Map;
importandroid.content.Context;
importandroid.database.Cursor;
importandroid.database.sqlite.SQLiteDatabase;
/**
* 业务bean
*
*/
publicclassFileService {
privateDBOpenHelper openHelper;
publicFileService(Context context) {
openHelper = newDBOpenHelper(context);
}
/**
* 获取每条线程已经下载的文件长度
* @param path
* @return
*/
publicMap getData(String path){
SQLiteDatabase db = openHelper.getReadableDatabase();
Cursor cursor = db.rawQuery("select threadid, downlength from SmartFileDownlog where downpath=?",newString[]{path});
Map data = newHashMap();
while(cursor.moveToNext()){
data.put(cursor.getInt(0), cursor.getInt(1));
}
cursor.close();
db.close();
returndata;
}
/**
* 保存每条线程已经下载的文件长度
* @param path
* @param map
*/
publicvoidsave(String path, Map map){//int threadid, int position
SQLiteDatabase db = openHelper.getWritableDatabase();
db.beginTransaction();
try{
for(Map.Entry entry : map.entrySet()){
db.execSQL("insert into SmartFileDownlog(downpath, threadid, downlength) values(?,?,?)",
newObject[]{path, entry.getKey(), entry.getValue()});
}
db.setTransactionSuccessful();
}finally{
db.endTransaction();
}
db.close();
}
/**
* 实时更新每条线程已经下载的文件长度
* @param path
* @param map
*/
publicvoidupdate(String path, Map map){
SQLiteDatabase db = openHelper.getWritableDatabase();
db.beginTransaction();
try{
for(Map.Entry entry : map.entrySet()){
db.execSQL("update SmartFileDownlog set downlength=? where downpath=? and threadid=?",
newObject[]{entry.getValue(), path, entry.getKey()});
}
db.setTransactionSuccessful();
}finally{
db.endTransaction();
}
db.close();
}
/**
* 当文件下载完成后,删除对应的下载记录
* @param path
*/
publicvoiddelete(String path){
SQLiteDatabase db = openHelper.getWritableDatabase();
db.execSQL("delete from SmartFileDownlog where downpath=?",newObject[]{path});
db.close();
}
}
packagecom.smart.impl;
importjava.io.File;
importjava.io.RandomAccessFile;
importjava.net.HttpURLConnection;
importjava.net.URL;
importjava.util.LinkedHashMap;
importjava.util.Map;
importjava.util.UUID;
importjava.util.concurrent.ConcurrentHashMap;
importjava.util.regex.Matcher;
importjava.util.regex.Pattern;
importandroid.content.Context;
importandroid.util.Log;
importcom.smart.db.FileService;
/**
* 文件下载器
* @author lihuoming@sohu.com
*/
publicclassSmartFileDownloader {
privatestaticfinalString TAG ="SmartFileDownloader";
privateContext context;
privateFileService fileService;
/* 已下载文件长度 */
privateintdownloadSize =0;
/* 原始文件长度 */
privateintfileSize =0;
/* 线程数 */
privateSmartDownloadThread[] threads;
/* 本地保存文件 */
privateFile saveFile;
/* 缓存各线程下载的长度*/
privateMap data =newConcurrentHashMap();
/* 每条线程下载的长度 */
privateintblock;
/* 下载路径 */
privateString downloadUrl;
/**
* 获取线程数
*/
publicintgetThreadSize() {
returnthreads.length;
}
/**
* 获取文件大小
* @return
*/
publicintgetFileSize() {
returnfileSize;
}
/**
* 累计已下载大小
* @param size
*/
protectedsynchronizedvoidappend(intsize) {
downloadSize += size;
}
/**
* 更新指定线程最后下载的位置
* @param threadId 线程id
* @param pos 最后下载的位置
*/
protectedvoidupdate(intthreadId,intpos) {
this.data.put(threadId, pos);
}
/**
* 保存记录文件
*/
protectedsynchronizedvoidsaveLogFile() {
this.fileService.update(this.downloadUrl,this.data);
}
/**
* 构建文件下载器
* @param downloadUrl 下载路径
* @param fileSaveDir 文件保存目录
* @param threadNum 下载线程数
*/
publicSmartFileDownloader(Context context, String downloadUrl, File fileSaveDir,intthreadNum) {
try{
this.context = context;
this.downloadUrl = downloadUrl;
fileService = newFileService(this.context);
URL url = newURL(this.downloadUrl);
if(!fileSaveDir.exists()) fileSaveDir.mkdirs();
this.threads =newSmartDownloadThread[threadNum];
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5*1000);
conn.setRequestMethod("GET");
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, */*");
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)thrownewRuntimeException("Unkown file size ");
String filename = getFileName(conn);
this.saveFile =newFile(fileSaveDir, filename);/* 保存文件 */
Map logdata = fileService.getData(downloadUrl);
if(logdata.size()>0){
for(Map.Entry entry : logdata.entrySet())
data.put(entry.getKey(), entry.getValue());
}
this.block = (this.fileSize %this.threads.length)==0?this.fileSize /this.threads.length :this.fileSize /this.threads.length +1;
if(this.data.size()==this.threads.length){
for(inti =0; i
this.downloadSize +=this.data.get(i+1);
}
print("已经下载的长度"+this.downloadSize);
}
}else{
thrownewRuntimeException("server no response ");
}
} catch(Exception e) {
print(e.toString());
thrownewRuntimeException("don't connection this url");
}
}
/**
* 获取文件名
*/
privateString getFileName(HttpURLConnection conn) {
String filename = this.downloadUrl.substring(this.downloadUrl.lastIndexOf('/') +1);
if(filename==null||"".equals(filename.trim())){//如果获取不到文件名称
for(inti =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())returnm.group(1);
}
}
filename = UUID.randomUUID()+ ".tmp";//默认取一个文件名
}
returnfilename;
}
/**
* 开始下载文件
* @param listener 监听下载数量的变化,如果不需要了解实时下载的数量,可以设置为null
* @return 已下载文件大小
* @throws Exception
*/
publicintdownload(SmartDownloadProgressListener listener)throwsException{
try{
RandomAccessFile randOut = newRandomAccessFile(this.saveFile,"rw");
if(this.fileSize>0) randOut.setLength(this.fileSize);
randOut.close();
URL url = newURL(this.downloadUrl);
if(this.data.size() !=this.threads.length){
this.data.clear();//清除数据
for(inti =0; i
this.data.put(i+1,0);
}
}
for(inti =0; i
intdownLength =this.data.get(i+1);
if(downLength
this.threads =newSmartDownloadThread(this, url,this.saveFile,this.block,this.data.get(i+1), i+1);
this.threads.setPriority(7);
this.threads.start();
}else{
this.threads =null;
}
}
this.fileService.save(this.downloadUrl,this.data);
booleannotFinish =true;//下载未完成
while(notFinish) {// 循环判断是否下载完毕
Thread.sleep(900);
notFinish = false;//假定下载完成
for(inti =0; i
if(this.threads !=null&& !this.threads.isFinish()) {
notFinish = true;//下载没有完成
if(this.threads.getDownLength() == -1){//如果下载失败,再重新下载
this.threads =newSmartDownloadThread(this, url,this.saveFile,this.block,this.data.get(i+1), i+1);
this.threads.setPriority(7);
this.threads.start();
}
}
}
if(listener!=null) listener.onDownloadSize(this.downloadSize);
}
fileService.delete(this.downloadUrl);
} catch(Exception e) {
print(e.toString());
thrownewException("file download fail");
}
returnthis.downloadSize;
}
/**
* 获取Http响应头字段
* @param http
* @return
*/
publicstaticMap getHttpResponseHeader(HttpURLConnection http) {
Map header = newLinkedHashMap();
for(inti =0;; i++) {
String mine = http.getHeaderField(i);
if(mine ==null)break;
header.put(http.getHeaderFieldKey(i), mine);
}
returnheader;
}
/**
* 打印Http头字段
* @param http
*/
publicstaticvoidprintResponseHeader(HttpURLConnection http){
Map header = getHttpResponseHeader(http);
for(Map.Entry entry : header.entrySet()){
String key = entry.getKey()!=null? entry.getKey()+":":"";
print(key+ entry.getValue());
}
}
//打印日志
privatestaticvoidprint(String msg){
Log.i(TAG, msg);
}
}
packagecom.smart.impl;
importjava.io.File;
importjava.io.InputStream;
importjava.io.RandomAccessFile;
importjava.net.HttpURLConnection;
importjava.net.URL;
importandroid.util.Log;
publicclassSmartDownloadThreadextendsThread {
privatestaticfinalString TAG ="SmartDownloadThread";
privateFile saveFile;
privateURL downUrl;
privateintblock;
/* *下载开始位置 */
privateintthreadId = -1;
privateintdownLength;
privatebooleanfinish =false;
privateSmartFileDownloader downloader;
publicSmartDownloadThread(SmartFileDownloader downloader, URL downUrl, File saveFile,intblock,intdownLength,intthreadId) {
this.downUrl = downUrl;
this.saveFile = saveFile;
this.block = block;
this.downloader = downloader;
this.threadId = threadId;
this.downLength = downLength;
}
@Override
publicvoidrun() {
if(downLength
try{
HttpURLConnection http = (HttpURLConnection) downUrl.openConnection();
http.setConnectTimeout(5*1000);
http.setRequestMethod("GET");
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, */*");
http.setRequestProperty("Accept-Language","zh-CN");
http.setRequestProperty("Referer", downUrl.toString());
http.setRequestProperty("Charset","UTF-8");
intstartPos = block * (threadId -1) + downLength;//开始位置
intendPos = block * threadId -1;//结束位置
http.setRequestProperty("Range","bytes="+ startPos +"-"+ endPos);//设置获取实体数据的范围
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");
InputStream inStream = http.getInputStream();
byte[] buffer =newbyte[1024];
intoffset =0;
print("Thread "+this.threadId +" start download from position "+ startPos);
RandomAccessFile threadfile = newRandomAccessFile(this.saveFile,"rwd");
threadfile.seek(startPos);
while((offset = inStream.read(buffer,0,1024)) != -1) {
threadfile.write(buffer, 0, offset);
downLength += offset;
downloader.update(this.threadId, downLength);
downloader.saveLogFile();
downloader.append(offset);
}
threadfile.close();
inStream.close();
print("Thread "+this.threadId +" download finish");
this.finish =true;
} catch(Exception e) {
this.downLength = -1;
print("Thread "+this.threadId+":"+ e);
}
}
}
privatestaticvoidprint(String msg){
Log.i(TAG, msg);
}
/**
* 下载是否完成
* @return
*/
publicbooleanisFinish() {
returnfinish;
}
/**
* 已经下载的内容大小
* @return 如果返回值为-1,代表下载失败
*/
publiclonggetDownLength() {
returndownLength;
}
}
packagecom.smart.activoty.download;
importjava.io.File;
importandroid.app.Activity;
importandroid.os.Bundle;
importandroid.os.Environment;
importandroid.os.Handler;
importandroid.os.Message;
importandroid.view.View;
importandroid.widget.Button;
importandroid.widget.EditText;
importandroid.widget.ProgressBar;
importandroid.widget.TextView;
importandroid.widget.Toast;
importcom.smart.impl.SmartDownloadProgressListener;
importcom.smart.impl.SmartFileDownloader;
publicclassSmartDownloadActivityextendsActivity {
privateProgressBar downloadbar;
privateEditText pathText;
privateTextView resultView;
privateHandler handler =newHandler(){
@Override//信息
publicvoidhandleMessage(Message msg) {
switch(msg.what) {
case1:
intsize = msg.getData().getInt("size");
downloadbar.setProgress(size);
floatresult = (float)downloadbar.getProgress()/ (float)downloadbar.getMax();
intp = (int)(result*100);
resultView.setText(p+"%");
if(downloadbar.getProgress()==downloadbar.getMax())
Toast.makeText(SmartDownloadActivity.this, R.string.success,1).show();
break;
case-1:
Toast.makeText(SmartDownloadActivity.this, R.string.error,1).show();
break;
}
}
};
@Override
publicvoidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button button = (Button)this.findViewById(R.id.button);
downloadbar = (ProgressBar)this.findViewById(R.id.downloadbar);
pathText = (EditText)this.findViewById(R.id.path);
resultView = (TextView)this.findViewById(R.id.result);
button.setOnClickListener(newView.OnClickListener() {
@Override
publicvoidonClick(View v) {
String path = pathText.getText().toString();
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
File dir = Environment.getExternalStorageDirectory();//文件保存目录
download(path, dir);
}else{
Toast.makeText(SmartDownloadActivity.this, R.string.sdcarderror,1).show();
}
}
});
}
//对于UI控件的更新只能由主线程(UI线程)负责,如果在非UI线程更新UI控件,更新的结果不会反映在屏幕上,某些控件还会出错
privatevoiddownload(finalString path,finalFile dir){
newThread(newRunnable() {
@Override
publicvoidrun() {
try{
SmartFileDownloader loader = newSmartFileDownloader(SmartDownloadActivity.this, path, dir,3);
intlength = loader.getFileSize();//获取文件的长度
downloadbar.setMax(length);
loader.download(newSmartDownloadProgressListener(){
@Override
publicvoidonDownloadSize(intsize) {//可以实时得到文件下载的长度
Message msg = newMessage();
msg.what = 1;
msg.getData().putInt("size", size);
handler.sendMessage(msg);
}});
} catch(Exception e) {
Message msg = newMessage();//信息提示
msg.what = -1;
msg.getData().putString("error","下载失败");//如果下载错误,显示提示失败!
handler.sendMessage(msg);
}
}
}).start();//开始
}
}