多线程下载文件的注意点主要有:
1、确定每个线程下载的文件大小,用这种方法计算:
先获取整个文件的大小length,然后除以线程个数count得到每个线程下载的块大小为blockSize = length / count,每个线程的起始下载点为:startPos = blockSize * i,结束下载点为:endPos = blockSize * (i + 1) - 1,其中最后一个线程下载的结束点应该为文件末尾
2、随机访问文件RandomAccessFile类的使用方法,该类的实例化需要两个参数,第一个可以使一个文件File类,也可以是文件的path,String类,第二个参数代表文件写入的模式,可以为"r","rw","rws","rwd"之一
3、在每个线程中写文件的时候都需要实例化一个RandomAccessFile的实例,而不能重复使用同一个RandomAccessFile
4、在设置请求参数时,获取服务器的响应码会有所不同,如果请求的是一整个资源,则请求成功的响应码是200,若请求的是某个资源的部分时,请求成功返回的响应码是206,这一点需要注意,另外,在每个线程中下载一个文件的不同部分时,需要设置请求的头字段:conn.setRequestProperty("Range", "bytes=12345-13456");设置该头字段代表请求服务器资源的12345到13456这部分的内容
另外,在每个线程下载之前,需要将RandomAccessFile的实例的指针指向下载的开始点,使用如下方法:
raf.seek(startPos); 其中raf为RandomAccessFile类的对象,startPos为下载的开始点
1、确定每个线程下载的文件大小,用这种方法计算:
先获取整个文件的大小length,然后除以线程个数count得到每个线程下载的块大小为blockSize = length / count,每个线程的起始下载点为:startPos = blockSize * i,结束下载点为:endPos = blockSize * (i + 1) - 1,其中最后一个线程下载的结束点应该为文件末尾
2、随机访问文件RandomAccessFile类的使用方法,该类的实例化需要两个参数,第一个可以使一个文件File类,也可以是文件的path,String类,第二个参数代表文件写入的模式,可以为"r","rw","rws","rwd"之一
3、在每个线程中写文件的时候都需要实例化一个RandomAccessFile的实例,而不能重复使用同一个RandomAccessFile
4、在设置请求参数时,获取服务器的响应码会有所不同,如果请求的是一整个资源,则请求成功的响应码是200,若请求的是某个资源的部分时,请求成功返回的响应码是206,这一点需要注意,另外,在每个线程中下载一个文件的不同部分时,需要设置请求的头字段:conn.setRequestProperty("Range", "bytes=12345-13456");设置该头字段代表请求服务器资源的12345到13456这部分的内容
另外,在每个线程下载之前,需要将RandomAccessFile的实例的指针指向下载的开始点,使用如下方法:
raf.seek(startPos); 其中raf为RandomAccessFile类的对象,startPos为下载的开始点
import java.io.File;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
public class Test {
/**
* 文件下载的路径
*/
private static final String PATH = "http://localhost:8080/test.exe";
/**
* 下载的线程个数
*/
private static final int COUNT = 3;
/**
* 创建一个文件,将远程文件写入该文件
*/
private File f = new File("C:\\Users\\Administrator\\Desktop\\test.exe");
public void download() throws Exception{
URL url = new URL(PATH);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//使用get请求
conn.setRequestMethod("GET");
//设置连接超时时间为5秒
conn.setConnectTimeout(5000);
//得到服务器返回的响应码
int code = conn.getResponseCode();
//响应码为200,代表请求资源成功(前提是请求整个资源,而不是资源的某一部分)
if(code == 200){
//得到文件的总大小
int length = conn.getContentLength();
//新建一个随机访问的文件
RandomAccessFile raf = new RandomAccessFile(f, "rw");
//第一个参数为文件类型,第二个参数为模式,可以为"r","rw","rws","rwd"
raf = new RandomAccessFile(f, "rw");
//设置随机文件的大小为服务器端文件的大小
raf.setLength(length);
System.out.println("文件总大小为:" + length);
//计算每个线程下载的块的大小
int blockSize = length / COUNT;
for(int i = 0; i < COUNT; i++){
//每个线程的起始下载点
int startPos = blockSize * i;
//每个线程的结束下载点
int endPos = blockSize * (i + 1) - 1;
//如果是最后一条线程,将其下载的终止点设为文件的终点
if(i == COUNT){
endPos = length;
}
System.out.println("线程" + i + "下载的部分为:" + startPos +"---" + endPos);
//开启线程分别下载不同的部分
new DownThread(i, startPos, endPos).start();
}
}
}
/**
* 下载文件的线程
* @author Administrator
*
*/
class DownThread extends Thread{
//线程ID
private int threadId;
//线程下载的起始点
private int startPos;
//线程下载的结束点
private int endPos;
public DownThread(int threadId, int startPos, int endPos) {
this.threadId = threadId;
this.startPos = startPos;
this.endPos = endPos;
}
@Override
public void run(){
try {
URL url = new URL(PATH);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
//设置头部的参数,表示请求服务器资源的某一部分
conn.setRequestProperty("Range", "bytes=" + startPos + "-" + endPos);
//设置了上面的头信息后,响应码为206代表请求资源成功,而不再是200
int code = conn.getResponseCode();
if(code == 206){
InputStream is = conn.getInputStream();
int hasRead = 0;
byte[] buf = new byte[1024];
//这里要注意新创建一个RandomAccessFile对象,而不能重复使用download方法中创建的
RandomAccessFile raf = new RandomAccessFile(f, "rw");
//将写文件的指针指向下载的起始点
raf.seek(startPos);
while((hasRead = is.read(buf)) > 0) {
raf.write(buf, 0, hasRead);
}
is.close();
System.out.println("线程" + threadId + "下载完毕...");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
try {
new Test().download();
} catch (Exception e) {
e.printStackTrace();
}
}
}