- 獲取網(wǎng)絡(luò)文件大小
關(guān)于inputStream.available()方法獲取下載文件的總大小
inputStream.available()獲取輸入流的大小,但是只有當(dāng)流不阻塞的時候才能用。網(wǎng)路不穩(wěn)定姓迅,read()方法是阻塞的般甲。要通過獲取
要通過httpconn.getContentLength();即
getHeaderFieldInt("Content-Length", -1);從解析頭即Content-length獲取大小
Closeable之帶資源的try語句 (jdk 1.7引入)
探究java IO之AutoCloseable,Closeable和Flushable接口
下載應(yīng)用肋乍,進(jìn)度更新,通知欄顯示
- 下載后軟件安裝敷存,安裝好后打開軟件
安裝軟件
if (beginDownload.getText().equals("安裝")){
Intent intent=new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file),"application/vnd.android.package-archive");//Type有哪些
startActivity(intent);
}
多線程分段下載墓造,斷點下載
如果我自己開發(fā)會有哪些疑問:
Q:下次下載怎么從上次保存的點下載,不同線程下載的怎么操作同一個文件
A:skipBytes(long i):從前往后,seek(long p): 從后往前锚烦,隨機(jī)訪問類Q:對一個文件要分幾個線程下載觅闽,每個線程怎么分配數(shù)據(jù)長度
A:
// 計算每條線程下載的數(shù)據(jù)長度,如果整除就平分,不能整除就直接進(jìn)1
this.block = (this.fileSize % this.threads.length) == 0 ? this.fileSize
/ this.threads.length
: this.fileSize / this.threads.length + 1;
- Q:怎么從服務(wù)器的獲取那個文件的指定位置開始下載
A:
block是長度每條線程下載的長度涮俄,downlength是已經(jīng)下載過的長度
int startPos = block * (threadId - 1) + downLength;// 開始位置
int endPos = block * threadId - 1;// 結(jié)束位置
http.setRequestProperty("Range", "bytes=" + startPos + "-"+ endPos);// 設(shè)置獲取實體數(shù)據(jù)的范圍
如果是第一次下載 downlength為0
比如19分兩條線程 block是10蛉拙, 1,0~9 2,10~19
或者18分三條線程 block是6,1,0~5 2,611彻亲,1217
參考帖子:
http://blog.csdn.net/wwj_748/article/details/20146869
-
用本地數(shù)據(jù)庫記錄不同線程上次下載的位置孕锄,根據(jù)這個位置去服務(wù)器獲取指定位置的字節(jié)。
HttpURLConnection http = (HttpURLConnection) downUrl .openConnection(); http.setConnectTimeout(5 * 1000); // 設(shè)置連接超時 http.setRequestMethod("GET"); // 設(shè)置請求方法苞尝,這里是“GET” // 瀏覽器可接受的MIME類型 http.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, */*"); http.setRequestProperty("Accept-Language", "zh-CN"); // 瀏覽器所希望的語言種類畸肆,當(dāng)服務(wù)器能夠提供一種以上的語言版本時要用到 http.setRequestProperty("Referer", downUrl.toString());// 包含一個URL,用戶從該URL代表的頁面出發(fā)訪問當(dāng)前請求的頁面宙址。 http.setRequestProperty("Charset", "UTF-8"); // 字符集 int startPos = block * (threadId - 1) + downLength;// 開始位置 int endPos = block * threadId - 1;// 結(jié)束位置 http.setRequestProperty("Range", "bytes=" + startPos + "-" + endPos);// 設(shè)置獲取實體數(shù)據(jù)的范圍 // 瀏覽器類型轴脐,如果Servlet返回的內(nèi)容與瀏覽器類型有關(guān)則該值非常有用。 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"); // 設(shè)置為持久連接 // 得到輸入流 InputStream inStream = http.getInputStream(); byte[] buffer = new byte[1024]; int offset = 0; print("Thread " + this.threadId + " start download from position " + startPos); // 隨機(jī)訪問文件 RandomAccessFile threadfile = new RandomAccessFile( this.saveFile, "rwd"); // 定位到pos位置 threadfile.seek(startPos); while (!downloader.getExit() && (offset = inStream.read(buffer, 0, 1024)) != -1) { // 寫入文件 threadfile.write(buffer, 0, offset); downLength += offset; // 累加下載的大小 downloader.update(this.threadId, downLength); // 更新指定線程下載最后的位置 downloader.append(offset); // 累加已下載大小 } threadfile.close(); inStream.close(); print("Thread " + this.threadId + " download finish"); this.finish = true;
緩存各線程下載的長度
Map<Integer, Integer> logdata = fileService
.getData(downloadUrl);// 獲取下載記錄
/* 緩存各線程下載的長度 */
private Map<Integer, Integer> data = new ConcurrentHashMap<Integer, Integer>();
for (Map.Entry<Integer, Integer> entry : logdata.entrySet()) //entryS et()獲取對象
data.put(entry.getKey(), entry.getValue());// 把各條線程已經(jīng)下載的數(shù)據(jù)長度放入data中
獲取文件名,獲取連接最后一個“/”后面的或者從HeaderField獲取
private String getFileName(HttpURLConnection conn) {
String filename = this.downloadUrl.substring(this.downloadUrl
.lastIndexOf('/') + 1);
if (filename == null || "".equals(filename.trim())) {// 如果獲取不到文件名稱
for (int i = 0;; i++) {
String mine = conn.getHeaderField(i);
if (mine == null)
break;
// content-disposition 就是當(dāng)用戶想把請求所得的內(nèi)容存為一個文件的時候提供一個默認(rèn)的文件名
if ("content-disposition".equals(conn.getHeaderFieldKey(i)
.toLowerCase())) {
//匹配字段獲取文件名,正則表達(dá)式
Matcher m = Pattern.compile(".*filename=(.*)").matcher(
mine.toLowerCase());
if (m.find())
return m.group(1);
}
}
filename = UUID.randomUUID() + ".tmp";// 默認(rèn)取一個文件名
}
return filename;
}
報頭的Content-disposition
就是當(dāng)用戶想把請求所得的內(nèi)容存為一個文件的時候提供一個默認(rèn)的文件名
就像用電腦的時候,彈窗讓用戶保存東西的時候有個默認(rèn)的名字
如果用于電腦豁辉,服務(wù)端要做的事:
1.當(dāng)代碼里面使用Content-Disposition來確保瀏覽器彈出下載對話框的時候令野。
response.addHeader("Content-Disposition","attachment");一定要確保沒有做過關(guān)于禁止瀏覽器緩存的操作。如下:
response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "No-cache");
response.setDateHeader("Expires", 0);
隨機(jī)訪問文件類RandomAccessFile
我覺得簡單來說就是 分段下載或者斷點下載的分割類徽级,可以看做 節(jié)點流
// 隨機(jī)訪問文件
RandomAccessFile threadfile = new RandomAccessFile(
this.saveFile, "rwd");
// 定位到pos位置
threadfile.seek(startPos);
有點切割字符串的方式切割文件打印字符串
白紙習(xí)題
|123456789|123456789|123456789|
1條記錄 2條記錄 3條記錄
//練習(xí)隨機(jī)訪問文件類
import java.io.*;
class Student
{
String name="aaaaaaa";
int age=0;
public static final int LEN=8;
public Student (String n,int a)
{
if(n.length( )>LEN)
{
n=n.substring(0,LEN);
}
if(n.length( )<LEN)
{
while(n.length( )<LEN)
{
n+="/u0000";//空格
}
}
this.name=n;
this.age=a;
}
}
class userText1
{
public static void main(String args[ ])throws Exception
{
Student stu1=new Student("Ada",23);
Student stu2=new Student("Shirlenjklcxvfchfhj",24);
Student stu3=new Student("sunfcvvcxvdfdas",25);
RandomAccessFile ra=new RandomAccessFile("student.txt","rw");//寫流
ra.write(stu1.name.getBytes( ));//第一的前8個字節(jié)气破,stu1 name屬性
//將當(dāng)前對象的name屬性的字符串轉(zhuǎn)為8字節(jié)數(shù)組
ra.write(stu1.age);//(文件的第九個字節(jié))將int類型的age以單字節(jié)的保存在文件中,占有一個字節(jié)
ra.write(stu2.name.getBytes( ));//第二對象的前8個字節(jié)餐抢,stu2 name屬性
ra.write(stu2.age);
ra.write(stu3.name.getBytes( ));//第三對象的前8個字節(jié)现使,stu3 name屬性
ra.write(stu3.age);
ra.close( );
int len=0;
byte buf[]=new byte[8];//長度為8的字節(jié)數(shù)組.
RandomAccessFile raf=new RandomAccessFile("student.txt","r");//讀流
//------------------------讀對象2屬性name,age
raf.skipBytes(9);//跳過9個字節(jié)
System.out.println (raf.getFilePointer( ));//指針位置
len=raf.read(buf);//##從文件當(dāng)中讀到的字節(jié)放在字節(jié)數(shù)組中最多只能放8個,并返回讀取字節(jié)的個數(shù)旷痕。
String str=null;//對象
str=new String(buf,0,len);//0-8//將字節(jié)數(shù)組buf[]中的全部內(nèi)容轉(zhuǎn)為String類型碳锈。
System.out.println (str+":"+raf.read( ));
//-------------------------讀對象1屬性name,age
raf.seek(0);//對指示器進(jìn)行決對定位
System.out.println (raf.getFilePointer( ));//指針位置
len=raf.read(buf);//讀取8個字節(jié)
str=new String(buf,0,len);
System.out.println (str+":"+raf.read( ));//age取一個字節(jié)
//--------------------------讀對象3屬性name,ag
raf.skipBytes(9);
System.out.println (raf.getFilePointer( ));//指針位置
len=raf.read(buf);//讀取8個字節(jié)
str=new String(buf,0,len);
System.out.println (str+":"+raf.read( ));
System.out.println (raf.getFilePointer( ));//指針位置
raf.close( );
}
}
多線程分段下載的意義:
網(wǎng)速帶寬是一定的,那么為什么多線程下載能加速欺抗?(TCP單流很難利用滿帶寬)
https://www.zhihu.com/question/19914902
F2 AS跳轉(zhuǎn)到錯誤處
Q:RandomAccessFile中mode,rws和rwd的區(qū)別售碳?
A:
先寫這個多線程分段下載
我是這么個步驟,由淺入深
1 我先寫了個下載
2 再寫單線程下載+暫停
3 最后才來寫多線程下載+暫停
瓶頸:
類的方法分類绞呈,比如一個是線程類贸人,一個是下載器類,在線程類處理哪些跟下載器處理哪些歸類不清晰
收獲:
思路很重要
對已 單線程下載+暫停
首次下載 獲取httpurlConnection佃声,connect艺智,獲取輸入流,創(chuàng)建本地文件圾亏,設(shè)置緩沖區(qū)大小十拣, 每次將緩沖的數(shù)據(jù)寫入文件,記錄暫停的位置志鹃,插入數(shù)據(jù)庫
SqlCreater sqlCreater = new SqlCreater(GetNetworkSize.this);
DbOperator dbOperator = new DbOperator(sqlCreater.getWritableDatabase());
HttpURLConnection urlConnection=Network.urlConnection(apkpath);
if (isFirst){
isFirst=false;
try {
InputStream in=urlConnection.getInputStream();
apksize=urlConnection.getContentLength()+"";
file=new File(apkpath);
FileOutputStream fo=new FileOutputStream(file);
byte[]b=new byte[1024];
int line=0;
while ((line=in.read())!=-1){
fo.write(b,0,line);
currentApksize+=line;
//第一次下載是添加
dbOperator.add(1,currentApksize);
p = (float) currentApksize / (float) Integer.valueOf(apksize) * 100;
pro = (int) p;
handler.sendEmptyMessage(SINGLEDOWN);
}
fo.close();
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
else{
//第二次下載是更新數(shù)據(jù)庫的內(nèi)容夭问,向服務(wù)器請求進(jìn)度, 并且拿到已經(jīng)保存的文件曹铃,根據(jù)進(jìn)度缰趋,隨機(jī)訪問文件下載
int startpos=dbOperator.getCurrentApkSize("multidownload");
try {
URL url=new URL(apkpath);
HttpURLConnection conn= (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setReadTimeout(5000);
conn.setRequestProperty("Range","byte="+startpos+"-"+apksize);
InputStream is=conn.getInputStream();
RandomAccessFile continuefile=new RandomAccessFile(file,"rwd");
byte[]b=new byte[1024];
int line=0;
while ((line=is.read())!=-1){
continuefile.write(b,0,line);
currentApksize+=line;
handler.sendEmptyMessage(SINGLEDOWN);
}
is.close();
} catch (IOException e) {
e.printStackTrace();
}
dbOperator.update(1,currentApksize);
}
}
非首次下載 獲取上次的位置startpos,向服務(wù)器獲取Range參數(shù)
conn.setRequestProperty("Range","byte="+startpos+"-"+apksize);
更新數(shù)據(jù)庫當(dāng)前線程的下載位置
繼續(xù)下載不需要再次調(diào)用connect方法铛只,已經(jīng)是connected狀態(tài)