黑马程序员技术交流社区

标题: java中多线程断点续传 [打印本页]

作者: 彭卫红    时间: 2014-8-30 23:57
标题: java中多线程断点续传
  1. import java.io.DataInputStream;
  2. import java.io.DataOutputStream;
  3. import java.io.File;
  4. import java.io.FileInputStream;
  5. import java.io.FileOutputStream;
  6. import java.io.IOException;
  7. import java.io.InputStream;
  8. import java.io.RandomAccessFile;
  9. import java.io.Serializable;
  10. import java.net.HttpURLConnection;
  11. import java.net.URL;

  12. //Java实现网络文件传输,在客户端请求Web服务器传输指定文件,并将文件保存。
  13. public class ResumeUpload {
  14.         private String downSource = "http://kent.dl.sourceforge.net/sourceforge/jamper/Sample.zip"; // 定义Web地址和文件名
  15.         private String savePath = "d:\\temp"; // 定义存文件路径
  16.         private String saveName = "汉仪YY字体.zip"; // 定义文件名
  17.         public ResumeUpload() {
  18.                 try {
  19.                         FileInfo bean = new FileInfo(downSource, savePath, saveName, 5);
  20.                         FTPthread fileFetch = new FTPthread(bean);
  21.                         fileFetch.start();
  22.                 } catch (Exception e) {
  23.                         e.printStackTrace();
  24.                 }
  25.         }
  26.         public static void main(String[] args) {
  27.                 new ResumeUpload();
  28.         }
  29. }
  30. class FTPthread extends Thread { // 传输文件线程类
  31.         FileInfo siteInfoBean = null; // 文件信息Bean
  32.         long[] nPos;
  33.         long[] startPos; // 开始位置
  34.         long[] endPos; // 结束位置
  35.         FilePart[] fileSplitterFetch; // 子线程对象
  36.         long nFileLength; // 文件长度
  37.         boolean bFirst = true; // 是否第一次取文件
  38.         boolean bStop = false; // 停止标志
  39.         File tmpFile; // 文件传输临时信息
  40.         DataOutputStream output; // 输出到文件的输出流
  41.         public FTPthread(FileInfo bean) throws IOException {
  42.                 siteInfoBean = bean;
  43.                 tmpFile = new File(bean.getSFilePath() + File.separator
  44.                                 + bean.getSFileName() + ".info");
  45.                 if (tmpFile.exists()) {
  46.                         bFirst = false;
  47.                         readInform();
  48.                 } else {
  49.                         startPos = new long[bean.getNSplitter()];
  50.                         endPos = new long[bean.getNSplitter()];
  51.                 }
  52.         }
  53.         public void run() {
  54.                 // 获得文件长度
  55.                 // 分割文件
  56.                 // 实例PartCacth
  57.                 // 启动PartCacth线程
  58.                 // 等待子线程返回
  59.                 try {
  60.                         if (bFirst) {
  61.                                 nFileLength = getFileSize();
  62.                                 if (nFileLength == -1) {
  63.                                         System.err.println("File Length is not known!");
  64.                                 } else if (nFileLength == -2) {
  65.                                         System.err.println("File is not access!");
  66.                                 } else {
  67.                                         for (int i = 0; i < startPos.length; i++) {
  68.                                                 startPos[i] = (long) (i * (nFileLength / startPos.length));
  69.                                         }
  70.                                         for (int i = 0; i < endPos.length - 1; i++) {
  71.                                                 endPos[i] = startPos[i + 1];
  72.                                         }
  73.                                         endPos[endPos.length - 1] = nFileLength;
  74.                                 }
  75.                         }
  76.                         // 启动子线程
  77.                         fileSplitterFetch = new FilePart[startPos.length];
  78.                         for (int i = 0; i < startPos.length; i++) {
  79.                                 fileSplitterFetch[i] = new FilePart(siteInfoBean.getSSiteURL(),
  80.                                                 siteInfoBean.getSFilePath() + File.separator
  81.                                                                 + siteInfoBean.getSFileName(), startPos[i],
  82.                                                 endPos[i], i);
  83.                                 AddInform.log("Thread " + i + " , 开始位置 = " + startPos[i]
  84.                                                 + ", 结束位置 = " + endPos[i]);
  85.                                 fileSplitterFetch[i].start();
  86.                         }
  87.                         // 等待子线程结束
  88.                         // int count = 0;
  89.                         // 是否结束while循环
  90.                         boolean breakWhile = false;
  91.                         while (!bStop) {
  92.                                 writeInform();
  93.                                 AddInform.sleep(500);
  94.                                 breakWhile = true;
  95.                                 for (int i = 0; i < startPos.length; i++) {
  96.                                         if (!fileSplitterFetch[i].bDownOver) {
  97.                                                 breakWhile = false;
  98.                                                 break;
  99.                                         }
  100.                                 }
  101.                                 if (breakWhile)
  102.                                         break;
  103.                         }
  104.                         System.out.println("文件传输结束!");
  105.                 } catch (Exception e) {
  106.                         e.printStackTrace();
  107.                 }
  108.         }
  109.         // 获得文件长度
  110.         public long getFileSize() {
  111.                 int nFileLength = -1;
  112.                 try {
  113.                         URL url = new URL(siteInfoBean.getSSiteURL());
  114.                         HttpURLConnection httpConnection = (HttpURLConnection) url
  115.                                         .openConnection();
  116.                         httpConnection.setRequestProperty("User-Agent", "NetFox");
  117.                         int responseCode = httpConnection.getResponseCode();
  118.                         if (responseCode >= 400) {
  119.                                 processErrorCode(responseCode);
  120.                                 return -2; // -2 为Web服务器响应错误
  121.                         }
  122.                         String sHeader;
  123.                         for (int i = 1;; i++) {
  124.                                 sHeader = httpConnection.getHeaderFieldKey(i);
  125.                                 if (sHeader != null) {
  126.                                         if (sHeader.equals("Content-Length")) {
  127.                                                 nFileLength = Integer.parseInt(httpConnection
  128.                                                                 .getHeaderField(sHeader));
  129.                                                 break;
  130.                                         }
  131.                                 } else
  132.                                         break;
  133.                         }
  134.                 } catch (IOException e) {
  135.                         e.printStackTrace();
  136.                 } catch (Exception e) {
  137.                         e.printStackTrace();
  138.                 }
  139.                 AddInform.log(nFileLength);
  140.                 return nFileLength;
  141.         }
  142.         // 保存传输信息(文件指针位置)
  143.         private void writeInform() {
  144.                 try {
  145.                         output = new DataOutputStream(new FileOutputStream(tmpFile));
  146.                         output.writeInt(startPos.length);
  147.                         for (int i = 0; i < startPos.length; i++) {
  148.                                 output.writeLong(fileSplitterFetch[i].startPos);
  149.                                 output.writeLong(fileSplitterFetch[i].endPos);
  150.                         }
  151.                         output.close();
  152.                 } catch (Exception e) {
  153.                         System.out.println("保存传输信息失败");
  154.                 }
  155.         }
  156.         // 读取保存的下载信息(文件指针位置)
  157.         private void readInform() {
  158.                 try {
  159.                         DataInputStream input = new DataInputStream(new FileInputStream(
  160.                                         tmpFile));
  161.                         int nCount = input.readInt();
  162.                         startPos = new long[nCount];
  163.                         endPos = new long[nCount];
  164.                         for (int i = 0; i < startPos.length; i++) {
  165.                                 startPos[i] = input.readLong();
  166.                                 endPos[i] = input.readLong();
  167.                         }
  168.                         input.close();
  169.                         // 判断每块的文件开始位置是否大于结束位置
  170.                         for (int i = 0; i < startPos.length; i++) {
  171.                                 if (startPos[i] > endPos[i]) {
  172.                                         startPos[i] = endPos[i];
  173.                                 }
  174.                         }
  175.                 } catch (Exception e) {
  176.                         System.out.println("读取保存的下载信息失败");
  177.                 }
  178.         }
  179.         private void processErrorCode(int nErrorCode) {
  180.                 System.err.println("Error Code : " + nErrorCode);
  181.         }
  182.         // 停止文件传输
  183.         public void doStop() {
  184.                 bStop = true;
  185.                 for (int i = 0; i < startPos.length; i++)
  186.                         fileSplitterFetch[i].splitterStop();
  187.         }
  188. }
  189. class FileInfo { // 定义获取和设置相关文件信息类
  190.         private String sSiteURL; // 定义URL变量
  191.         private String sFilePath; // 定义存文件路径变量
  192.         private String sFileName; // 定义文件名变量
  193.         private int nSplitter; // 定义传输文件计数值
  194.         public FileInfo() {
  195.                 this("", "", "", 5); // 设置传输文件计数值
  196.         }
  197.         public FileInfo(String sURL, String sPath, String sName, int nSpiltter) {
  198.                 sSiteURL = sURL;
  199.                 sFilePath = sPath;
  200.                 sFileName = sName;
  201.                 this.nSplitter = nSpiltter;
  202.         }
  203.         public String getSSiteURL() {
  204.                 return sSiteURL;
  205.         }
  206.         public void setSSiteURL(String value) {
  207.                 sSiteURL = value;
  208.         }
  209.         public String getSFilePath() {
  210.                 return sFilePath;
  211.         }
  212.         public void setSFilePath(String value) {
  213.                 sFilePath = value;
  214.         }
  215.         public String getSFileName() {
  216.                 return sFileName;
  217.         }
  218.         public void setSFileName(String value) {
  219.                 sFileName = value;
  220.         }
  221.         public int getNSplitter() {
  222.                 return nSplitter;
  223.         }
  224.         public void setNSplitter(int nCount) {
  225.                 nSplitter = nCount;
  226.         }
  227. }
  228. class FilePart extends Thread {
  229.         String sURL; // 定义文件传输时使用的变量
  230.         long startPos; // 分段文件传输开始位置
  231.         long endPos; // 分段文件传输结束位置
  232.         int nThreadID; // 子线程ID
  233.         boolean bDownOver = false; // 完成文件传输
  234.         boolean bStop = false; // 停止文件传输
  235.         SaveFile fileAccess = null;
  236.         public FilePart(String sURL, String sName, long nStart, long nEnd, int id)
  237.                         throws IOException {
  238.                 this.sURL = sURL;
  239.                 this.startPos = nStart;
  240.                 this.endPos = nEnd;
  241.                 nThreadID = id;
  242.                 fileAccess = new SaveFile(sName, startPos);
  243.         }
  244.         public void run() {
  245.                 while (startPos < endPos && !bStop) {
  246.                         try {
  247.                                 URL url = new URL(sURL);
  248.                                 HttpURLConnection httpConnection = (HttpURLConnection) url
  249.                                                 .openConnection();
  250.                                 httpConnection.setRequestProperty("User-Agent", "NetFox");
  251.                                 String sProperty = "bytes=" + startPos + "-";
  252.                                 httpConnection.setRequestProperty("RANGE", sProperty);
  253.                                 AddInform.log(sProperty);
  254.                                 InputStream input = httpConnection.getInputStream();
  255.                                 byte[] b = new byte[1024];
  256.                                 int nRead;
  257.                                 while ((nRead = input.read(b, 0, 1024)) > 0
  258.                                                 && startPos < endPos && !bStop) {
  259.                                         startPos += fileAccess.write(b, 0, nRead);
  260.                                 }
  261.                                 AddInform.log("Thread " + nThreadID + " is over!");
  262.                                 bDownOver = true;
  263.                         } catch (Exception e) {
  264.                                 System.out.println(getName() + " 线程运行异常");
  265.                         }
  266.                 }
  267.                 bDownOver = true;
  268.         }
  269.         public void logResponseHead(HttpURLConnection con) {
  270.                 for (int i = 1;; i++) {
  271.                         String header = con.getHeaderFieldKey(i);
  272.                         if (header != null)
  273.                                 AddInform.log(header + " : " + con.getHeaderField(header));
  274.                         else
  275.                                 break;
  276.                 }
  277.         }
  278.         public void splitterStop() {
  279.                 bStop = true;
  280.         }
  281. }
  282. class SaveFile implements Serializable { // 定义访问文件类
  283.         RandomAccessFile oSavedFile;
  284.         long nPos;
  285.         public SaveFile() throws IOException {
  286.                 this("", 0);
  287.         }
  288.         public SaveFile(String sName, long nPos) throws IOException {
  289.                 oSavedFile = new RandomAccessFile(sName, "rw");
  290.                 this.nPos = nPos;
  291.                 oSavedFile.seek(nPos);
  292.         }
  293.         public synchronized int write(byte[] b, int nStart, int nLen) {
  294.                 int n = -1;
  295.                 try {
  296.                         oSavedFile.write(b, nStart, nLen);
  297.                         n = nLen;
  298.                 } catch (IOException e) {
  299.                         System.out.println("同步存储信息异常");
  300.                 }
  301.                 return n;
  302.         }
  303. }
  304. class AddInform { // 定义输出提示信息及线程sleep类
  305.         public AddInform() {
  306.         }
  307.         public static void sleep(int nSecond) {
  308.                 try {
  309.                         Thread.sleep(nSecond);
  310.                 } catch (Exception e) {
  311.                         e.printStackTrace();
  312.                 }
  313.         }
  314.         public static void log(String sMsg) {
  315.                 System.out.println(sMsg);
  316.         }
  317.         public static void log(int sMsg) {
  318.                 System.out.println(sMsg);
  319.         }
  320. }
复制代码





欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2