📄 downloader.java
字号:
/* * To change this template, choose Tools | Templates * and open the template in the editor. */package biz.tbuy.huliqing.jloading.downloader;import biz.tbuy.huliqing.jloading.Elog;import biz.tbuy.huliqing.jloading.TaskManager;import java.io.File;import java.io.IOException;import java.net.URL;import java.util.ArrayList;import java.util.List;import java.util.Timer;import java.util.TimerTask;import java.util.Vector;/** * * @author huliqing */public class Downloader { private Config config; // 状态保存配置文件 private String taskid; // 任务标识 private String name; // 短文件名(不包含后缀及路径) private String url; // 下载源地址 private File fileSave; // 保存路径 private File fileProcess;//正在下载中的文件名(默认比原文件名多带一个"_") private int threads; // 下载的线程数 private boolean isFirst = true; // 是否为首次下载(需要创建状态文件) private boolean isInit = false; // 是否已经初始化. private long totalBytes;// 总文件大小 private long readBytes; // 已经读取的大小 private long readBytesNow; // 当次启动后所读的字节数 // 待完成的任务列表 private Vector<Piece> tasks = new Vector<Piece>(); // 当前的所有任务列表,包括正在处理中的与待完成的任务列表 private Vector<Piece> tasksAll = new Vector<Piece>(); // 空闲的任务线程 private Vector<PieceLoader> frees = new Vector<PieceLoader>(); // 所有执行线程,包括执行的与空闲的 private List<PieceLoader> loaders = new ArrayList<PieceLoader>(); // 运行状态信息 private int state = 0; private final static int STATE_NONE = 0; private final static int STATE_READY = 1; private final static int STATE_LOADING = 2; private final static int STATE_PAUSED = 3; private final static int STATE_STOPPED = 4; //private final static int STATE_OK = 5; // -------------------------------------------------- 构造 /** * 使用状态文件进行断点续传 * @param config */ public Downloader(Config config) { this.config = config; taskid = config.getId(); name = config.getName(); url = config.getURLs().get(0); fileSave = new File(config.getSave()); threads = config.getThreads(); totalBytes = config.getLength(); // 1 文件长度 List<Piece> pieces = config.loadPieces(); // 2 任务列表 //System.out.println("pieces=" + pieces.size()); for (Piece p : pieces) { tasksAll.add(p); tasks.add(p); } // 初始化已读文件的长度,将文件长度减去未读的文件长度,即是已读的长度 for (Piece p : tasksAll) { readBytes += (p.getPos() - p.getStart()); } readBytes += pieces.size(); //System.out.println("readBytes=" + readBytes); // 标识为“已初始化”“续传操作(因有Config文件)” isInit = true; isFirst = false; state = STATE_READY; } /** * 新建任务方式进行下载 * @param name * @param url * @param save * @param threads * @throws java.lang.Exception */ public Downloader(String taskid, String name, String url, String save, int threads) { this.taskid = taskid; this.name = name; this.url = url; this.fileSave = new File(save); this.threads = threads; new Thread(new Runnable(){ public void run() { init0(); } }).start(); } /** 首次下载文件(即非使用Config文件的方式),需要初始化相关信息 */ private void init0() { // 对文件进行分块 try { totalBytes = new URL(url).openConnection().getContentLength(); if (totalBytes == -1) state = STATE_NONE; } catch (IOException ioe) { return; } // 创建分块,并创建相应的负责下载的线程 long pieceSize = (long) Math.ceil((double) totalBytes / (double) threads); long pStart = 0; long pEnd = 0; tasksAll.clear(); tasks.clear(); for (int i = 0; i < threads; i++) { if (i == 0) { pStart = pieceSize * i; } if (i == threads - 1) { pEnd = totalBytes; } else { pEnd = pStart + pieceSize; } Piece piece = new Piece(pStart, pStart, pEnd); tasksAll.add(piece); tasks.add(piece); pStart = pEnd + 1; } // 标识为“已初始化”“首次下载” isInit = true; isFirst = true; state = STATE_READY; } // 创建状态保存文件 private synchronized void checkAndCreateConfig() { int n = 0; try { // 判断是否为首次下载,如果是,则需要判断是否存在同名的文件,若存在同 // 名的文件,则重新更新保存路径 if (isFirst) { String path = fileSave.getAbsolutePath(); String f_head = path.substring(0, path.lastIndexOf(".")); String f_suffix = path.substring(path.lastIndexOf(".")); // 含"." File f1 = new File(path); // 最终保存文件 File f2 = new File(path + "_"); // 未完成任务之前的文件名 File f3 = new File(path + "_config"); // 状态保存文件 // 检查并确保不存在相同名称的文件名,saveChange标识(存在相同的文件名) while (f1.exists() || f2.exists() || f3.exists()) { n++; path = f_head + "[" + n + "]" + f_suffix; f1 = new File(path); f2 = new File(path + "_"); f3 = new File(path + "_config"); } try { f2.createNewFile(); } catch (Exception e) { } fileSave = f1; fileProcess = f2; isFirst = false; // 创建状态文件,并保存,字段分别为 // 任务标识, 文件名,下载源,保存路径,文件大小, 线程数 config = ConfigFactory.createConfig( taskid, name, url, fileSave.getAbsolutePath(), totalBytes, threads); config.savePieces(tasksAll); // 更新任务列表中的信息 TaskManager.getInstance().updateConfigPath(config); } else { fileProcess = new File(fileSave.getAbsolutePath() + "_"); } } catch (Exception e) { Elog.log("出错:创建配置文件是遇到问题!" + getClass().getName()); } } private void loading() { // 初始化下载线程(先清除,以确保没有不可用的线程) loaders.clear(); for (int i = 0; i < threads; i++) { PieceLoader pl = new PieceLoader(this, tasks); pl.start(); loaders.add(pl); } // 初始化定时器,每10秒保存一次状态 timer = new Timer(); timer.schedule(new MyTimerTask(), 0, 1000 * 10); } // ------------------------------------------------------------ 状态信息 /** 状态未知 */ public boolean isNone() { return state == STATE_NONE ? true : false; } /** 是否运行中的 */ public boolean isLoading() { return state == STATE_LOADING ? true : false; } /** 是否准备就绪的 */ public boolean isReady() { return state == STATE_READY ? true : false; } /** 是否暂停中的 */ public boolean isPaused() { return state == STATE_PAUSED ? true : false; } /** 是否已经停止的 */ public boolean isStopped() { return state == STATE_STOPPED ? true : false; } /** 判断文件是否已经全部下载完 */ public synchronized boolean isOk() { return (readBytes >= totalBytes); } // ------------------------------------------------------------ 任务控制 /** 开始下载任务 */ public void toStart() { if (isLoading()) { //System.out.println("正在运行中"); return; } else if (isPaused()) { // 暂停中的... //System.out.println("暂停转为运行..."); state = STATE_LOADING; for (PieceLoader pl : loaders) { pl.toContinue(); } } else { //System.out.println("直接运行"); // 如果还从未下载过数据,则完全初始化 if (!isInit) init0(); // 检查相应的文件是否存在,确保不会出现重名文件 checkAndCreateConfig(); // 开始下载 state = STATE_LOADING; loading(); } } /** 暂停任务 */ public void toPause() { state = STATE_PAUSED; } /** 停止任务,如果任务已经完成,则重命名文件名*/ public synchronized void toStop() { try { if (isLoading() || isPaused()) { timer.cancel(); config.savePieces(tasksAll); state = STATE_STOPPED; for (PieceLoader pl : loaders) { pl.interrupt(); } } } catch (Exception exception) { } } /** 删除下载任务 */ public boolean toDelete() { toStop(); if (fileProcess != null && fileProcess.exists()) { fileProcess.delete(); } if (config != null) { config.delete(); TaskManager.getInstance().deleteConfig(config.getId()); } TaskManager.getInstance().removeTask(taskid); return true; } /** 处理已经完成的任务 */ public synchronized void processWhenOk() { timer.cancel(); fileProcess.renameTo(fileSave); config.delete(); TaskManager.getInstance().deleteConfig(config.getId()); TaskManager.getInstance().removeTask(taskid); //System.out.println("切片数:" + tasksAll.size()); //System.out.println("读取数据ReadBytes:" + readBytes); } // ------------------------------------------------------------ 任务处理 /** 将一个新的任务区域(待完成的)添加到列表中,tasks, tasksAll */ public synchronized void addTask(Piece piece) { this.tasksAll.add(piece); this.tasks.add(piece); config.savePieces(tasksAll); //System.out.println("tasksAll.size=" + tasksAll.size()); //System.out.println("tasks.size=" + tasks.size()); } /** * 增加已读的字节数,下载线程每次读写完一段数据后都会调用该方法 * @param readBytes */ public synchronized void growReadBytes(long bytes) { readBytesNow += bytes; readBytes += bytes; } /** 是否有空的任务线程 */ public synchronized boolean isFreeLoader() { return frees.size() > 0; } /** 添加一个空闲的任务线程 */ public synchronized void addFreeLoader(PieceLoader pl) { frees.add(pl); } /** 移除一个空闲线程 */ public synchronized void removeFreeLoader(PieceLoader pl) { frees.remove(pl); } // ------------------------------------------------------------ 任务状态信息 /** 获取任务的URL下载源地址 */ public String getURL() { return url; } /** 获取下载任务的最终文件保存对象 */ public File getFileSave() { return fileSave; } /** 获取下载中的文件对象 */ public File getFileProcess() { return fileProcess; } /** 获取当次启动后已经读取的字节 */ public long getReadBytesNow() { return readBytesNow; } /** 已经读取的总字节数(包括续传的) */ public long getReadBytes() { return readBytes; } /** 获取总文件长度 */ public long getTotalBytes() { return totalBytes; } /** 获取下载速度 (k/s) */ public long getSpeed() { long sp = 0; for (PieceLoader pl : loaders) { sp += pl.getSpeed(); } return sp; } // ------------------------------------------------------------ 偏移修正 private int repairCount; // 修正次数 private long offsetTotal; // 偏移量 public long getOffsetTotal() { return offsetTotal; } public synchronized void setOffsetTotal(long offsetTotal) { this.offsetTotal = offsetTotal; } public int getRepairCount() { return repairCount; } public synchronized void setRepairCount(int repairCount) { this.repairCount = repairCount; } // ------------------------------------------------------------ 定时器 /** * 任务定时器,每隔一段时间,这个定时器会把当前下载任务的各个分片(Piece)的状态 * 信息保存到磁盘文件中,以避免程序出现意外或突然蹦溃而造成的无法断点续传的功能 */ private Timer timer; private class MyTimerTask extends TimerTask { @Override public void run() { try { if (isOk()) this.cancel(); config.savePieces(tasksAll); } catch (Exception ex) { //Elog.log("MyTimerTask"); } } } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -