?? 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; // 狀態(tài)保存配置文件 private String taskid; // 任務標識 private String name; // 短文件名(不包含后綴及路徑) private String url; // 下載源地址 private File fileSave; // 保存路徑 private File fileProcess;//正在下載中的文件名(默認比原文件名多帶一個"_") private int threads; // 下載的線程數(shù) private boolean isFirst = true; // 是否為首次下載(需要創(chuàng)建狀態(tài)文件) private boolean isInit = false; // 是否已經(jīng)初始化. private long totalBytes;// 總文件大小 private long readBytes; // 已經(jīng)讀取的大小 private long readBytesNow; // 當次啟動后所讀的字節(jié)數(shù) // 待完成的任務列表 private Vector<Piece> tasks = new Vector<Piece>(); // 當前的所有任務列表,包括正在處理中的與待完成的任務列表 private Vector<Piece> tasksAll = new Vector<Piece>(); // 空閑的任務線程 private Vector<PieceLoader> frees = new Vector<PieceLoader>(); // 所有執(zhí)行線程,包括執(zhí)行的與空閑的 private List<PieceLoader> loaders = new ArrayList<PieceLoader>(); // 運行狀態(tài)信息 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; // -------------------------------------------------- 構造 /** * 使用狀態(tài)文件進行斷點續(xù)傳 * @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); // 標識為“已初始化”“續(xù)傳操作(因有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; } // 創(chuàng)建分塊,并創(chuàng)建相應的負責下載的線程 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; } // 創(chuàng)建狀態(tài)保存文件 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"); // 狀態(tài)保存文件 // 檢查并確保不存在相同名稱的文件名,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; // 創(chuàng)建狀態(tài)文件,并保存,字段分別為 // 任務標識, 文件名,下載源,保存路徑,文件大小, 線程數(shù) 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("出錯:創(chuàng)建配置文件是遇到問題!" + 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秒保存一次狀態(tài) timer = new Timer(); timer.schedule(new MyTimerTask(), 0, 1000 * 10); } // ------------------------------------------------------------ 狀態(tài)信息 /** 狀態(tài)未知 */ 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; } /** 是否已經(jīng)停止的 */ public boolean isStopped() { return state == STATE_STOPPED ? true : false; } /** 判斷文件是否已經(jīng)全部下載完 */ 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("直接運行"); // 如果還從未下載過數(shù)據(jù),則完全初始化 if (!isInit) init0(); // 檢查相應的文件是否存在,確保不會出現(xiàn)重名文件 checkAndCreateConfig(); // 開始下載 state = STATE_LOADING; loading(); } } /** 暫停任務 */ public void toPause() { state = STATE_PAUSED; } /** 停止任務,如果任務已經(jīng)完成,則重命名文件名*/ 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; } /** 處理已經(jīng)完成的任務 */ public synchronized void processWhenOk() { timer.cancel(); fileProcess.renameTo(fileSave); config.delete(); TaskManager.getInstance().deleteConfig(config.getId()); TaskManager.getInstance().removeTask(taskid); //System.out.println("切片數(shù):" + tasksAll.size()); //System.out.println("讀取數(shù)據(jù)ReadBytes:" + readBytes); } // ------------------------------------------------------------ 任務處理 /** 將一個新的任務區(qū)域(待完成的)添加到列表中,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()); } /** * 增加已讀的字節(jié)數(shù),下載線程每次讀寫完一段數(shù)據(jù)后都會調(diào)用該方法 * @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); } // ------------------------------------------------------------ 任務狀態(tài)信息 /** 獲取任務的URL下載源地址 */ public String getURL() { return url; } /** 獲取下載任務的最終文件保存對象 */ public File getFileSave() { return fileSave; } /** 獲取下載中的文件對象 */ public File getFileProcess() { return fileProcess; } /** 獲取當次啟動后已經(jīng)讀取的字節(jié) */ public long getReadBytesNow() { return readBytesNow; } /** 已經(jīng)讀取的總字節(jié)數(shù)(包括續(xù)傳的) */ 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; // 修正次數(shù) 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)的狀態(tài) * 信息保存到磁盤文件中,以避免程序出現(xiàn)意外或突然蹦潰而造成的無法斷點續(xù)傳的功能 */ 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 + -