JavaSE多线程下载的实现

时间:2022-11-11 18:34:32

本文中主要提供的是java多线程下载文件方案,以及java多线程将临时进度保存到文件,多线程断点续传的实现;

1、多线程下载
2、将下载进度保存到临时文件,多线程的断定续传


1、多线程下载
本例中首先在Tomcat服务器中的WEBAPP/ROOT/文件夹下面放置了SoftwareOffer.exe的二进制可执行文件,如果放置图片的话,中间数据如果出错,不容易用肉眼识别,但是如果是二进制文件的话,如果中间任何二进制的一位数据出错,必然造成二进制可执行文件无法运行!所以测试选择二进制可执行文件比较妥当!
代码如下:


  • MultiDownloader.java
import java.net.HttpURLConnection;
import java.net.URL;
/**
* 多线程下载器
* @author YQ
*/

public class MultiDownloader {

//总线程数
private final static int totalThreadCount = 3;

@SuppressWarnings("unused")
public static void main(String[] args) throws Exception {
System.out.println("多线程下载器");
URL url = new URL("http://192.168.1.102:8080/SoftwareOffer.exe");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
int code = conn.getResponseCode();
if(code == 200) {
int length = conn.getContentLength();
System.out.println("服务器资源文件长度:"+length);
int blockSize = length / 3;//服务器资源等分为三份,每一份的大小相同
System.out.println("资源文件每一份的到小:"+blockSize);

for(int threadid = 0;threadid < totalThreadCount;threadid++) {
int startPosition = threadid * blockSize;
int endPosition = (threadid+1) * blockSize - 1;

if(threadid == (totalThreadCount - 1)) {
endPosition = length - 1;
}
System.out.println("线程:" + threadid + "下载范围:" + startPosition + "~" + endPosition);

byte[] buffer = new byte[blockSize];
}

}
}

}

2、将下载进度保存到临时文件,多线程的断定续传
本例中同样在Tomcat服务器中的WEBAPP/ROOT/文件夹下面放置了SoftwareOffer.exe的二进制可执行文件;


  • DemoDownLoad.java
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

/**
* 将下载进度保存到临时文件
* 多线程的断定续传
* @author YQ
*
*/


public class DemoDownLoad {
static String path = "http://192.168.1.102:8080/SoftwareOffer.exe";
static int threadCount = 3;
static int finishedThread = 0;
public static void main(String[] args) {
//发送http请求,拿到目标文件的长度
try {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(8000);
conn.setReadTimeout(8000);

if(conn.getResponseCode() == 200) {
//获取长度
int length = conn.getContentLength();//算出进程的开始位置和结束位置

//创建临时文件
File file = new File(getNameFormPath(path));
//临时文件的使用
RandomAccessFile raf = new RandomAccessFile(file,"rwd");
//设置临时文件的大小与目标文件相互一致
raf.setLength(length);
raf.close();
//计算每一个线程下载的区间
int size = length / threadCount;

for (int id=0;id < threadCount;id++) {
int startIndex = id * size;
int endIndex = (id + 1) * size - 1;
if(id == threadCount - 1) {
endIndex = length - 1;
}
System.out.println("线程" + id + "下载区间" + startIndex + "-" + endIndex);
new DownLoadThread(id, startIndex, endIndex).start();
}
}
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

public static String getNameFormPath(String path) {
int index = path.lastIndexOf("/");
return path.substring(index + 1);
}
}

class DownLoadThread extends Thread {

int threadId;
int startIndex;
int endIndex;

public DownLoadThread(int threadId, int startIndex, int endIndex) {
super();
this.threadId = threadId;
this.startIndex = startIndex;
this.endIndex = endIndex;
}

@Override
public void run() {
//发送Http请求,三次下载则是需要三次下载请求
try {
File fileProgress = new File(threadId+".txt");
int lastProgress = 0;
if(fileProgress.exists()) {
//读取进度临时文件中内容
FileInputStream fis = new FileInputStream(fileProgress);
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
//得到上一次的下载进度数据
lastProgress = Integer.parseInt(br.readLine());
//改变下载开始位置,上一次下过的文件,这一次就不再进行请求
startIndex += lastProgress;
//进度临时文件的输入流必须关闭
fis.close();
}

URL url = new URL(DemoDownLoad.path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(8000);
conn.setReadTimeout(8000);

//设置请求数据的区间
conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);

//请求部分数据成功的响应码是206
if(conn.getResponseCode() == 206) {
InputStream is = conn.getInputStream();
byte[] bytes = new byte[1024];
int len = 0;
File file = new File(DemoDownLoad.getNameFormPath(DemoDownLoad.path));
RandomAccessFile raf = new RandomAccessFile(file, "rwd");
//设置写入文件的开始位置
raf.seek(startIndex);
//当前线程下载的总进度
int total = lastProgress;
while((len = is.read(bytes)) != -1) {
raf.write(bytes,0,len);
total += len;
System.out.println("线程"+threadId+"下载了:"+total);
//创建一个文本临时文件,保存下载进度
RandomAccessFile rafProgress = new RandomAccessFile(fileProgress, "rwd");
//每次下载1024个字节,就马上把1024写入文本的临时文件
rafProgress.write((total+"").getBytes());
rafProgress.close();
}
raf.close();
System.out.println("线程"+threadId+"下载完成");

DemoDownLoad.finishedThread++;
//首先必须要保证三条线程全部都下载完毕才可以删除
synchronized (DemoDownLoad.path) {
if(DemoDownLoad.finishedThread == 3) {
//可能会出现线程不安全
for (int i=0;i<DemoDownLoad.finishedThread;i++) {
File f = new File(i+".txt");
f.delete();
}
DemoDownLoad.finishedThread = 0;
}
}
}

} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}