浏览器请求头说明
HTTP 请求头 Range
请求资源的部分内容(不包括响应头的大小),单位是byte,即字节,从0开始.
如果服务器能够正常响应的话,服务器会返回 206 Partial Content 的状态码及说明.
如果不能处理这种Range的话,就会返回整个资源以及响应状态码为 200 OK .
(这个要注意,要分段下载时,要先判断这个)
Range 请求头格式
Range: bytes=start-end
Range: bytes=10- :第10个字节及最后个字节的数据
Range: bytes=40-100 :第40个字节到第100个字节之间的数据.
响应头说明
Content-Range: bytes 0-10/3103 //这个表示,服务器响应了前(0-10)个字节的数据,该资源一共有(3103)个字节大小。
Content-Type: image/png //表示这个资源的类型
Content-Length: 11 //表示这次服务器响应了11个字节的数据(0-10)
Last-Modified: Tue, 30 Jun 2015 03:12:48 GMT //表示资源最近修改的时间(分段下载时要注意这个东西,因为如果修改了,分段下载可能就要重新下载了)
ETag: W/"3103-1435633968000" //这个响应头表示资源版本的标识符,通常是消息摘要(类似MD5一样)
第一步 先获取要下载的文件大小
private static long getFileSize() {
HttpURLConnection connection
= null
;
HttpURLConnection conn
= null
;
try {
conn
= (HttpURLConnection
) new URL(url
).openConnection();
conn
.setConnectTimeout(3000);
conn
.setRequestMethod("HEAD");
conn
.connect();
String headerField
= conn
.getHeaderField("Content-Disposition");
System
.out
.println("Content-Disposition:" + headerField
);
String eTag
= conn
.getHeaderField("ETag");
System
.out
.println("ETag:" + eTag
);
String contentType
= conn
.getContentType();
System
.out
.println("contentType:" + contentType
);
System
.out
.println("* 连接服务器成功");
} catch (MalformedURLException e
) {
throw new RuntimeException("URL错误");
} catch (IOException e
) {
System
.err
.println("x 连接服务器失败[" + e
.getMessage() + "]");
return -1;
}
return conn
.getContentLengthLong();
}
第二步根据线程计算每个线程要下载的数据
private static String url
= "http://a.xzfile.com/down4/bitcometv1.71_downcc.com.zip";
private static int thnum
=5;
public static void main(String
[] args
) throws IOException
{
File f
= new File(getFileName());
boolean newFile
= f
.createNewFile();
RandomAccessFile rw
= new RandomAccessFile(f
.getName(), "rw");
FileChannel channel
= rw
.getChannel();
long fileSize
= getFileSize();
System
.out
.println("文件大小:"+fileSize
);
System
.out
.println(f
.getAbsolutePath());
System
.out
.println(f
.getCanonicalPath());
int c
= (int) (fileSize
/thnum
);
for (int i
= 0; i
<thnum
; i
++) {
int begin
= i
*c
;
int end
=begin
+c
;
if(i
== (thnum
-1)){
end
= (int) fileSize
;
}
MyDownloadTask myDownloadTask
= new MyDownloadTask(url
,getFileName(),(int)fileSize
,begin
,end
);
Thread t
= new Thread(myDownloadTask
);
t
.start();
}
}
第三步每个线程下载自己要下载都数据
public class MyDownloadTask implements Runnable {
private String url
;
private final RandomAccessFile file
;
private final FileChannel channel
;
private int begin
;
private int end
;
private int pageSize
;
public MyDownloadTask(String url
, String fileName
, int pageSize
,int begin
,int end
) throws FileNotFoundException
{
this.url
= url
;
this.file
= new RandomAccessFile(fileName
, "rw");
this.channel
= file
.getChannel();
this.pageSize
= pageSize
;
this.begin
=begin
;
this.end
=end
;
}
@SneakyThrows
@Override
public void run() {
InputStream connect
= connect();
byte [] bytes
= new byte[1024];
int read
;
ByteArrayOutputStream bos
= new ByteArrayOutputStream();
while ((read
=connect
.read(bytes
))!=-1){
bos
.write(bytes
,0,read
);
}
ByteBuffer wrap
= ByteBuffer
.wrap(bos
.toByteArray());
channel
.write(wrap
,begin
);
System
.out
.println(Thread
.currentThread().getName()+":下载完成");
}
private InputStream
connect() throws IOException
{
HttpURLConnection conn
= (HttpURLConnection
) new URL(url
).openConnection();
conn
.setConnectTimeout(300000);
conn
.setRequestMethod("GET");
conn
.setRequestProperty("Range", "bytes=" + begin
+ "-"+end
);
conn
.connect();
int statusCode
= conn
.getResponseCode();
if (HttpURLConnection
.HTTP_PARTIAL
!= statusCode
) {
conn
.disconnect();
throw new IOException("状态码错误:" + statusCode
);
}
System
.out
.println(Thread
.currentThread().getName()+"服务器响应字节:" + conn
.getHeaderField("Content-Length"));
return conn
.getInputStream();
}
}