//这个是任务Bean
public class Task {
private String downURL;
private String saveFile;
private int bufferSize = 64 * 1024;
private int workerCount;
private int sectionCount;
private long contentLength;
private long[] sectionsOffset;
public static final int HEAD_SIZE = 4096;
//读下载描述文件内容
public synchronized void read(RandomAccessFile file) throws IOException {
byte[] temp = new byte[HEAD_SIZE];
file.seek(0);
int readed = file.read(temp);
if (readed != temp.length) {
throw new RuntimeException();
}
ByteArrayInputStream bais = new ByteArrayInputStream(temp);
DataInputStream dis = new DataInputStream(bais);
downURL = dis.readUTF();
saveFile = dis.readUTF();
sectionCount = dis.readInt();
contentLength = dis.readLong();
sectionsOffset = new long[sectionCount];
for (int i = 0; i < sectionCount; i++) {
sectionsOffset[i] = file.readLong();
}
}
//创建下载描述文件内容
public synchronized void create(RandomAccessFile file) throws IOException {
if (sectionCount != sectionsOffset.length) {
throw new RuntimeException();
}
long len = HEAD_SIZE + 8 * sectionCount;
file.setLength(len);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
dos.writeUTF(downURL);
dos.writeUTF(saveFile);
dos.writeInt(sectionCount);
dos.writeLong(contentLength);
byte[] src = baos.toByteArray();
byte[] temp = new byte[HEAD_SIZE];
System.arraycopy(src, 0, temp, 0, src.length);
file.seek(0);
file.write(temp);
writeOffset(file);
}
//更新下载的过程
public synchronized void writeOffset(RandomAccessFile file) throws IOException {
if (sectionCount != sectionsOffset.length) {
throw new RuntimeException();
}
file.seek(HEAD_SIZE);
for (int i = 0; i < sectionsOffset.length; i++) {
file.writeLong(sectionsOffset[i]);
}
}
(下面是Getter、Setter)
}
//这个是下载主程序
public class TaskAssign {
public void work(Task task) throws IOException {
File file = new File(task.getSaveFile());
if (file.exists()) {
return;
}
//这个是记录是否下载成功。我这里也没有增加失败回复、重试之类的工作。
final AtomicBoolean success = new AtomicBoolean(true);
//任务描述文件
File taskFile = new File(task.getSaveFile() + ".r_task");
//真正下载的文件
File saveFile = new File(task.getSaveFile() + ".r_save");
boolean taskFileExist = taskFile.exists();
RandomAccessFile taskRandomFile = null;
RandomAccessFile downRandomFile = null;
try {
taskRandomFile = new RandomAccessFile(taskFile, "rw");
downRandomFile = new RandomAccessFile(saveFile, "rw");
long rtnLen = getContentLength(task.getDownURL());
if (!taskFileExist) {
//如果文件不存在,就初始化任务文件和下载文件
task.setContentLength(rtnLen);
initTaskFile(taskRandomFile, task);
downRandomFile.setLength(rtnLen);
} else {
//任务文件存在就读取任务文件
task.read(taskRandomFile);
if (task.getContentLength() != rtnLen) {
throw new RuntimeException();
}
}
int secCount = task.getSectionCount();
//分配线程去下载,这里用到线程池
ExecutorService es = Executors.newFixedThreadPool(task.getWorkerCount());
for (int i = 0; i < secCount; i++) {
final int j = i;
final Task t = task;
final RandomAccessFile f1 = taskRandomFile;
final RandomAccessFile f2 = downRandomFile;
es.execute(new Runnable() {
public void run() {
try {
down(f1, f2, t, j);
} catch (IOException e) {
success.set(false);
e.printStackTrace(System.out);
}
}
});
}
es.shutdown();
try {
es.awaitTermination(24 * 3600, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
taskRandomFile.close();
taskRandomFile = null;
downRandomFile.close();
downRandomFile = null;
//如果下载成功,去掉任务描述文件、帮下载文件改名
if (success.get()) {
taskFile.delete();
saveFile.renameTo(file);
}
} finally {
if (taskRandomFile != null) {
taskRandomFile.close();
taskRandomFile = null;
}
if (downRandomFile != null) {
downRandomFile.close();
downRandomFile = null;
}
}
}
public void down(RandomAccessFile taskRandomFile, RandomAccessFile downRandomFile, Task task, int sectionNo) throws IOException {
//这里我用HttpURLConnection下载,你也可以用HttpClient或者自己实现一个Http协议(不过貌似没有必要)
URL u = new URL(task.getDownURL());
HttpURLConnection conn = (HttpURLConnection) u.openConnection();
long start = task.getSectionsOffset()[sectionNo];
long end = -1;
//这里要注意一下,这里是计算当前块的长度
if (sectionNo < task.getSectionCount() - 1) {
long per = task.getContentLength() / task.getSectionCount();
end = per * (sectionNo + 1);
} else {
end = task.getContentLength();
}
if (start >= end) {
System.out.println("Section has finished before. " + sectionNo);
return;
}
String range = "bytes=" + start + "-" + (end - 1);
conn.setRequestProperty("Range", range);
conn.setRequestProperty("User-Agent", "Ray-Downer");
try {
conn.connect();
if (conn.getResponseCode() != 206) {
throw new RuntimeException();
}
if (conn.getContentLength() != (end - start)) {
throw new RuntimeException();
}
InputStream is = conn.getInputStream();
byte[] temp = new byte[task.getBufferSize()];
BufferedInputStream bis = new BufferedInputStream(is, temp.length);
int readed = 0;
while ((readed = bis.read(temp)) > 0) {
long offset = task.getSectionsOffset()[sectionNo];
synchronized (task) {
//下载之后顺便更新描述文件,你可能会发现这里效率比较低,在一个线程同步里进行两次文件操作。你可以自己实现一个缓冲写。
downRandomFile.seek(offset);
downRandomFile.write(temp, 0, readed);
offset += readed;
task.getSectionsOffset()[sectionNo] = offset;
task.writeOffset(taskRandomFile);
}
}
} finally {
conn.disconnect();
}
System.out.println("Section finished. " + sectionNo);
}
public void initTaskFile(RandomAccessFile taskRandomFile, Task task) throws IOException {
int secCount = task.getSectionCount();
long per = task.getContentLength() / secCount;
long[] sectionsOffset = new long[secCount];
for (int i = 0; i < secCount; i++) {
sectionsOffset[i] = per * i;
}
task.setSectionsOffset(sectionsOffset);
task.create(taskRandomFile);
}
public long getContentLength(String url) throws IOException {
URL u = new URL(url);
HttpURLConnection conn = (HttpURLConnection) u.openConnection();
try {
return conn.getContentLength();
} finally {
conn.disconnect();
}
}
}
//稍微测试一下。
public class Main {
public static void main(String[] args) throws IOException {
test3();
System.out.println("\n\n===============\nFinished.");
}
public static void test1() throws IOException {
Task task = new Task();
task.setDownURL("http://61.152.235.21/qqfile/qq/2007iistable/QQ2007IIKB1.exe");
task.setSaveFile("H:/Test2.exe");
task.setSectionCount(200);
task.setWorkerCount(100);
task.setBufferSize(256 * 1024);
TaskAssign ta = new TaskAssign();
ta.work(task);
}
public static void test2() throws IOException {
Task task = new Task();
task.setDownURL("http://student1.scut.edu.cn:8880/manage/news/data/1208421861893.xls");
task.setSaveFile("H:/Test3.xls");
task.setSectionCount(5);
task.setWorkerCount(1);
task.setBufferSize(128 * 1024);
TaskAssign ta = new TaskAssign();
ta.work(task);
}
public static void test3() throws IOException {
Task task = new Task();
task.setDownURL("http://go.microsoft.com/fwlink/?linkid=57034");
task.setSaveFile("H:/vc2005express.iso");
task.setSectionCount(500);
task.setWorkerCount(200);
task.setBufferSize(128 * 1024);
TaskAssign ta = new TaskAssign();
ta.work(task);
}
public static void test4() throws IOException {
Task task = new Task();
task.setDownURL("http://down.sandai.net/Thunder5.7.9.472.exe");
task.setSaveFile("H:/Thunder.exe");
task.setSectionCount(30);
task.setWorkerCount(30);
task.setBufferSize(128 * 1024);
TaskAssign ta = new TaskAssign();
ta.work(task);
}
}
分享到:
相关推荐
在这个WindowsApplicationTest中,我们可以推测它可能是一个包含Java Swing应用程序的测试项目,用于演示上述的多线程断点下载功能。通过运行这个程序,开发者可以直观地看到多线程下载的效率提升以及断点续传的便利...
总的来说,这个Java多线程断点续传下载程序是一个综合性的实践项目,它涵盖了Java编程的多个重要方面,对于提升你的技能和理解并发编程有极大的帮助。通过这个项目,你不仅可以学习到多线程编程的原理,还能掌握如何...
Java多线程断点下载文件是一种高效的文件下载方式,它允许在下载过程中暂停并从上次停止的地方继续,尤其适用于大文件下载。以下是实现这一功能的关键知识点: 1. **获取文件信息**: - 使用`java.net.URL`和`java...
Java多线程断点下载是网络编程中一个实用的技术,尤其在处理...总的来说,Java多线程断点下载是一个涉及并发编程、文件操作、异常处理等多个领域的综合实践。通过合理设计和优化,我们可以构建出高效且可靠的下载系统。
标题中的“多线程断点续传程序 Java版本”指的是一个使用Java编程语言实现的软件,该软件具备多线程下载功能,并且支持在下载中断后从上次停止的位置继续下载,即断点续传。这样的特性对于大文件下载或者网络环境不...
Java多线程断点续传下载是一个复杂但实用的技术,尤其在处理大文件或网络不稳定时,能够提高用户体验并优化资源利用。以下是对这个主题的详细解析: **1. Java多线程** Java多线程是指在一个Java应用程序中同时执行...
以下是一个简单的多线程断点续传的步骤概述: 1. **初始化状态**:检查目标文件是否存在,如果存在且非空,读取其大小作为断点。如果不存在,那么断点为零。 2. **文件分块**:根据文件大小和预期的并发线程数,将...
Java多线程断点下载是Java编程中一种高级技术,主要应用于大文件的网络传输,如视频、软件安装包等。这种技术结合了多线程和断点续传的概念,提高了下载效率并允许用户在中断后继续从上次停止的位置开始下载。 首先...
【OKHttp多线程断点下载】是一种在Android或Java应用中实现高效文件下载的方法,它结合了OKHttp网络库的优秀性能与多线程技术,以提高下载速度,并允许在下载过程中中断和恢复,避免因网络问题或其他因素导致的下载...
"htp多线程断点下载文件"这一主题,涉及了网络编程、多线程技术和文件处理等多个知识点。 首先,HTTP(超文本传输协议)是我们浏览网页和下载文件的基础,它是应用层协议,负责客户端和服务器之间的通信。然而,...
通过以上步骤,我们可以构建一个功能完备的Java多线程断点续传下载程序。这个项目不仅可以帮助初学者理解多线程和网络编程的基本概念,也可以作为实际项目开发中的一个参考模板。对于想要深入研究Java网络编程和并发...
在实现一个多线程断点续传下载程序时,以下是一些关键步骤: 1. **初始化**: 开始时,程序需要检查本地是否有已下载的部分,如果有,读取保存的文件偏移量作为下载起点。 2. **创建线程池**: 根据网络条件和文件...
在这个“基于Java的多线程断点续传程序”中,我们主要关注的是如何利用Java的多线程特性和网络编程能力来实现文件的断点续传功能。这个程序可能是为了满足在不完整的下载过程中能够继续从上次中断的位置开始下载的...
Java多线程断点下载的核心思想是将大文件分割成多个小块,每个小块由一个独立的线程负责下载,最终合并所有小块来重构原始文件。这种方法可以充分利用网络带宽,提高下载效率,同时具备断点续传功能,即使在下载过程...
总之,Java多线程断点下载是一个涉及到文件操作、网络通信、线程同步和异常处理等多个领域的复杂任务,但通过合理的设计和实现,可以显著提升大文件下载的效率和用户体验。在实际项目中,可以参考开源库如`Volley`、...
总结来说,Java实现的多线程断点续传涉及的技术点包括: 1. 并发编程:使用`ExecutorService`、`CountDownLatch`进行线程管理和同步。 2. 文件操作:分析和合并临时文件,实现断点续传。 3. 网络I/O:通过`...
然而,实现多线程断点续传需要解决几个问题: 1. **同步管理**:多个线程可能会同时访问同一个文件的部分,因此需要使用`synchronized`关键字或`Lock`对象来确保并发安全。 2. **断点信息共享**:每个线程需要知道...
在这个特定的项目中,我们关注的是一个实现多线程断点续传功能的Java应用程序,它还具有用户界面来选择保存文件的目录,并显示下载进度条。这个程序的实现涉及到以下几个关键知识点: 1. **Socket编程**:Java中的...
【Java多线程简单下载器】是一个初学者的编程作业,虽然代码可能较为混乱,但其核心功能已经实现,即通过多线程技术进行文件的下载。在Java中,多线程是并发处理的重要手段,它允许多个任务在同一时间执行,从而提高...