Java实现的断点续传功能的示例代码
程序员文章站
2024-03-06 08:23:19
代码中已经加入了注释,需要的朋友可以直接参考代码中的注释。下面直接上功能实现的主要代码:
import java.io.file;
import java....
代码中已经加入了注释,需要的朋友可以直接参考代码中的注释。下面直接上功能实现的主要代码:
import java.io.file; import java.io.filenotfoundexception; import java.io.ioexception; import java.io.inputstream; import java.io.randomaccessfile; import java.net.httpurlconnection; import java.net.malformedurlexception; import java.net.url; import java.util.concurrent.countdownlatch; import java.util.concurrent.executorservice; import java.util.concurrent.executors; /* * encode:utf-8 * * author:zhiming.xu * * 多线程的断点下载程序,根据输入的url和指定线程数,来完成断点续传功能。 * * 每个线程支负责某一小段的数据下载;再通过randomaccessfile完成数据的整合。 */ public class multitheraddownload { private string filepath = null; private string filename = null; private string tmpfilename = null; private int threadnum = 0; private countdownlatch latch = null;//设置一个计数器,代码内主要用来完成对缓存文件的删除 private long filelength = 0l; private long threadlength = 0l; private long[] startpos;//保留每个线程下载数据的起始位置。 private long[] endpos;//保留每个线程下载数据的截止位置。 private boolean bool = false; private url url = null; //有参构造函数,先构造需要的数据 public multitheraddownload(string filepath, int threadnum) { this.filepath = filepath; this.threadnum = threadnum; startpos = new long[this.threadnum]; endpos = new long[this.threadnum]; latch = new countdownlatch(this.threadnum); } /* * 组织断点续传功能的方法 */ public void downloadpart() { file file = null; file tmpfile = null; httpurlconnection httpcon = null; //在请求url内获取文件资源的名称;此处没考虑文件名为空的情况,此种情况可能需使用uuid来生成一个唯一数来代表文件名。 filename = filepath.substring(filepath.lastindexof('/') + 1, filepath .contains("?") ? filepath.lastindexof('?') : filepath.length()); tmpfilename = filename + "_tmp"; try { url = new url(filepath); httpcon = (httpurlconnection) url.openconnection(); setheader(httpcon); filelength = httpcon.getcontentlengthlong();//获取请求资源的总长度。 file = new file(filename); tmpfile = new file(tmpfilename); threadlength = filelength / threadnum;//每个线程需下载的资源大小。 system.out.println("filename: " + filename + " ," + "filelength= " + filelength + " the threadlength= " + threadlength); if (file.exists() && file.length() == filelength) { system.out .println("the file you want to download has exited!!"); return; } else { setbreakpoint(startpos, endpos, tmpfile); executorservice exec = executors.newcachedthreadpool(); for (int i = 0; i < threadnum; i++) { exec.execute(new downloadthread(startpos[i], endpos[i], this, i, tmpfile, latch)); } latch.await();//当你的计数器减为0之前,会在此处一直阻塞。 exec.shutdown(); } } catch (malformedurlexception e) { e.printstacktrace(); } catch (ioexception e) { e.printstacktrace(); } catch (interruptedexception e) { e.printstacktrace(); } if (file.length() == filelength) { if (tmpfile.exists()) { system.out.println("delect the temp file!!"); tmpfile.delete(); } } } /* * 断点设置方法,当有临时文件时,直接在临时文件中读取上次下载中断时的断点位置。没有临时文件,即第一次下载时,重新设置断点。 * * rantmpfile.seek()跳转到一个位置的目的是为了让各个断点存储的位置尽量分开。 * * 这是实现断点续传的重要基础。 */ private void setbreakpoint(long[] startpos, long[] endpos, file tmpfile) { randomaccessfile rantmpfile = null; try { if (tmpfile.exists()) { system.out.println("the download has continued!!"); rantmpfile = new randomaccessfile(tmpfile, "rw"); for (int i = 0; i < threadnum; i++) { rantmpfile.seek(8 * i + 8); startpos[i] = rantmpfile.readlong(); rantmpfile.seek(8 * (i + 1000) + 16); endpos[i] = rantmpfile.readlong(); system.out.println("the array content in the exit file: "); system.out.println("thre thread" + (i + 1) + " startpos:" + startpos[i] + ", endpos: " + endpos[i]); } } else { system.out.println("the tmpfile is not available!!"); rantmpfile = new randomaccessfile(tmpfile, "rw"); //最后一个线程的截止位置大小为请求资源的大小 for (int i = 0; i < threadnum; i++) { startpos[i] = threadlength * i; if (i == threadnum - 1) { endpos[i] = filelength; } else { endpos[i] = threadlength * (i + 1) - 1; } rantmpfile.seek(8 * i + 8); rantmpfile.writelong(startpos[i]); rantmpfile.seek(8 * (i + 1000) + 16); rantmpfile.writelong(endpos[i]); system.out.println("the array content: "); system.out.println("thre thread" + (i + 1) + " startpos:" + startpos[i] + ", endpos: " + endpos[i]); } } } catch (filenotfoundexception e) { e.printstacktrace(); } catch (ioexception e) { e.printstacktrace(); } finally { try { if (rantmpfile != null) { rantmpfile.close(); } } catch (ioexception e) { e.printstacktrace(); } } } /* * 实现下载功能的内部类,通过读取断点来设置向服务器请求的数据区间。 */ class downloadthread implements runnable { private long startpos; private long endpos; private multitheraddownload task = null; private randomaccessfile downloadfile = null; private int id; private file tmpfile = null; private randomaccessfile rantmpfile = null; private countdownlatch latch = null; public downloadthread(long startpos, long endpos, multitheraddownload task, int id, file tmpfile, countdownlatch latch) { this.startpos = startpos; this.endpos = endpos; this.task = task; this.tmpfile = tmpfile; try { this.downloadfile = new randomaccessfile(this.task.filename, "rw"); this.rantmpfile = new randomaccessfile(this.tmpfile, "rw"); } catch (filenotfoundexception e) { e.printstacktrace(); } this.id = id; this.latch = latch; } @override public void run() { httpurlconnection httpcon = null; inputstream is = null; int length = 0; system.out.println("the thread " + id + " has started!!"); while (true) { try { httpcon = (httpurlconnection) task.url.openconnection(); setheader(httpcon); //防止网络阻塞,设置指定的超时时间;单位都是ms。超过指定时间,就会抛出异常 httpcon.setreadtimeout(20000);//读取数据的超时设置 httpcon.setconnecttimeout(20000);//连接的超时设置 if (startpos < endpos) { //向服务器请求指定区间段的数据,这是实现断点续传的根本。 httpcon.setrequestproperty("range", "bytes=" + startpos + "-" + endpos); system.out .println("thread " + id + " the total size:---- " + (endpos - startpos)); downloadfile.seek(startpos); if (httpcon.getresponsecode() != httpurlconnection.http_ok && httpcon.getresponsecode() != httpurlconnection.http_partial) { this.task.bool = true; httpcon.disconnect(); downloadfile.close(); system.out.println("the thread ---" + id + " has done!!"); latch.countdown();//计数器自减 break; } is = httpcon.getinputstream();//获取服务器返回的资源流 long count = 0l; byte[] buf = new byte[1024]; while (!this.task.bool && (length = is.read(buf)) != -1) { count += length; downloadfile.write(buf, 0, length); //不断更新每个线程下载资源的起始位置,并写入临时文件;为断点续传做准备 startpos += length; rantmpfile.seek(8 * id + 8); rantmpfile.writelong(startpos); } system.out.println("the thread " + id + " total load count: " + count); //关闭流 is.close(); httpcon.disconnect(); downloadfile.close(); rantmpfile.close(); } latch.countdown();//计数器自减 system.out.println("the thread " + id + " has done!!"); break; } catch (ioexception e) { e.printstacktrace(); } finally { try { if (is != null) { is.close(); } } catch (ioexception e) { e.printstacktrace(); } } } } } /* * 为一个httpurlconnection模拟请求头,伪装成一个浏览器发出的请求 */ private void setheader(httpurlconnection con) { con.setrequestproperty( "user-agent", "mozilla/5.0 (x11; u; linux i686; en-us; rv:1.9.0.3) gecko/2008092510 ubuntu/8.04 (hardy) firefox/3.0.3"); con.setrequestproperty("accept-language", "en-us,en;q=0.7,zh-cn;q=0.3"); con.setrequestproperty("accept-encoding", "aa"); con.setrequestproperty("accept-charset", "iso-8859-1,utf-8;q=0.7,*;q=0.7"); con.setrequestproperty("keep-alive", "300"); con.setrequestproperty("connection", "keep-alive"); con.setrequestproperty("if-modified-since", "fri, 02 jan 2009 17:00:05 gmt"); con.setrequestproperty("if-none-match", "\"1261d8-4290-df64d224\""); con.setrequestproperty("cache-control", "max-age=0"); con.setrequestproperty("referer", "http://www.skycn.com/soft/14857.html"); } }
下面是测试代码:
public class downloadtest { /** * @param args */ public static void main(string[] args) { string filepath = "http://127.0.0.1:8080/file/loadfile.mkv"; multitheraddownload load = new multitheraddownload(filepath ,4); load.downloadpart(); } }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。