最新下载
热门教程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
Java实现的断点续传功能的示例代码
时间:2022-06-29 01:40:39 编辑:袖梨 来源:一聚教程网
代码如下 | 复制代码 |
importjava.io.File; importjava.io.FileNotFoundException; importjava.io.IOException; importjava.io.InputStream; importjava.io.RandomAccessFile; importjava.net.HttpURLConnection; importjava.net.MalformedURLException; importjava.net.URL; importjava.util.concurrent.CountDownLatch; importjava.util.concurrent.ExecutorService; importjava.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模拟请求头,伪装成一个浏览器发出的请求 */ privatevoidsetHeader(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"); } } |
下面是测试代码:
代码如下 | 复制代码 |
publicclassDownLoadTest {
/** * @param args */ publicstaticvoidmain(String[] args) {
String filepath ="http://127.0.0.1:8080/file/loadfile.mkv"; MultiTheradDownLoad load =newMultiTheradDownLoad(filepath ,4); load.downloadPart(); } } |
相关文章
- 人们熟悉的寄居蟹属于以下哪种分类 神奇海洋11月21日答案 11-21
- 第五人格11.22共研服有什么更新 11月22日共研服更新内容介绍 11-21
- 原神恰斯卡怎么培养 11-21
- 无期迷途四星装束是谁 11-21
- 王者荣耀帝丹高中校服怎么获得 11-21
- 光遇姆明季后续版本怎么玩 11-21