Описание заголовка запроса браузера
Диапазон заголовка HTTP-запроса
请求资源的部分内容(不包括响应头的大小),单位是byte,即字节,从0开始.
如果服务器能够正常响应的话,服务器会返回 206 Partial Content 的状态码及说明.
如果不能处理这种Range的话,就会返回整个资源以及响应状态码为 200 OK .
(这个要注意,要分段下载时,要先判断这个)
Формат заголовка запроса диапазона
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();
}
}