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
<
Integer, Integer
>
data
=
new
ConcurrentHashMap
<
Integer, Integer
>
();
/*
每条线程下载的大小
*/
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
<
Integer, Integer
>
logdata
=
fileService.getData(downloadUrl);
if
(logdata.size()
>
0
){
for
(Map.Entry
<
Integer, Integer
>
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
)
<
this
.fileSize){
//
该线程未完成下载时,继续下载
RandomAccessFile randOut
=
new
RandomAccessFile(
this
.saveFile,
"
rw
"
);
if
(
this
.fileSize
>
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
*/
public
static
Map
<
String, String
>
getHttpResponseHeader(HttpURLConnection http) {
Map
<
String, String
>
header
=
new
LinkedHashMap
<
String, String
>
();
for
(
int
i
=
0
;; i
++
) {
String mine
=
http.getHeaderField(i);
if
(mine
==
null
)
break
;
header.put(http.getHeaderFieldKey(i), mine);
}
return
header;
}
/**
* 打印Http头字段
*
@param
http
*/
public
static
void
printResponseHeader(HttpURLConnection http){
Map
<
String, String
>
header
=
getHttpResponseHeader(http);
for
(Map.Entry
<
String, String
>
entry : header.entrySet()){
String key
=
entry.getKey()
!=
null
?
entry.getKey()
+
"
:
"
:
""
;
print(key
+
entry.getValue());
}
}
private
static
void
print(String msg){
Log.i(TAG, msg);
}
public
static
void
main(String[] args) {
/*
FileDownloader loader = new FileDownloader(context, "
http://browse.babasport.com/ejb3/ActivePort.exe
",
new File("D://androidsoft//test"), 2);
loader.getFileSize();//得到文件总大小
try {
loader.download(new DownloadProgressListener(){
public void onDownloadSize(int size) {
print("已经下载:"+ size);
}
});
} catch (Exception e) {
e.printStackTrace();
}
*/
}
}