以多线程、断点续传方式下载文件的实现

以多线程、断点续传方式下载文件,经常出现下载下来的文件大小和服务端一致,但是却无法正常打开的现象,搞了很久,贴下我的实现方式,请各位多多指教

思路:

1、将下载文件的处理放在自定义的线程类中,每下载一个文件就新启动一个下载线程。

2、在下载线程中完成对服务端的链接和身份认证,成功后开始下载文件。

3、新建n个子线程,根据下载文件的大小和线程数量得到每个子线程要下载的大小。

4、分别启动子线程,进行分段下载。

5、分段下载完成,合并临时文件。

6、合并文件完成,删除临时文件。

 

实现:

FTP下载线程类

Java代码
  1. package com.jfc.ftp.tools;  
  2.   
  3. import java.io.BufferedInputStream;  
  4. import java.io.BufferedOutputStream;  
  5. import java.io.File;  
  6. import java.io.FileInputStream;  
  7. import java.io.FileOutputStream;  
  8. import java.util.concurrent.locks.Lock;  
  9. import java.util.concurrent.locks.ReentrantLock;  
  10.   
  11. import com.jfc.ftp.service.FTPService;  
  12. import com.jfc.ftp.util.Constant;  
  13. import com.jfc.ftp.util.PropertyUtil;  
  14.   
  15. /** 
  16.  * 为断点续传下载文件而启动新的线程 
  17.  * @author SavageGarden 
  18.  * 
  19.  */  
  20. public class FTPThread extends Thread{  
  21.     /** 
  22.      * 站点URL 
  23.      */  
  24.     private String host;  
  25.     /** 
  26.      * 站点端口 
  27.      */  
  28.     private int port;  
  29.     /** 
  30.      * 用户 
  31.      */  
  32.     private String user;  
  33.     /** 
  34.      * 密码 
  35.      */  
  36.     private String pswd;  
  37.     /** 
  38.      * 当前线程的FTP操作接口实现类 
  39.      */  
  40.     private FTPService ftpService;  
  41.   
  42.     /** 
  43.      * 第几个下载项 
  44.      */  
  45.     private int rowIndex;  
  46.     /** 
  47.      * 要下载的文件路径 
  48.      */  
  49.     private String filepath;  
  50.     /** 
  51.      * 要下载的文件大小 
  52.      */  
  53.     private long filesize;  
  54.     /** 
  55.      * 要下载的文件保存路径 
  56.      */  
  57.     private String savepath;  
  58.       
  59.     /** 
  60.      * 标记文件已下载量 
  61.      */  
  62.     public int hadRead = 0;  
  63.     /** 
  64.      * 下载线程开始时间 
  65.      */  
  66.     public long startTime = 0;  
  67.     /** 
  68.      * 下载线程结束时间 
  69.      */  
  70.     public long endTime = 0;  
  71.     /** 
  72.      * 当前下载线程的互斥锁 
  73.      */  
  74.     public Lock ftpThreadLock;  
  75.     /** 
  76.      * 当前下载线程的状态 
  77.      */  
  78.     private int status = Constant.THREAD_STATUS_NEW;  
  79.       
  80.     public synchronized int getStatus() {  
  81.         return status;  
  82.     }  
  83.     public synchronized void setStatus(int status) {  
  84.         this.status = status;  
  85.     }  
  86.     /** 
  87.      * 是否已经合并文件 
  88.      */  
  89.     private boolean hadMerger = false;  
  90.       
  91.     public synchronized boolean isHadMerger() {  
  92.         return hadMerger;  
  93.     }  
  94.     public synchronized void setHadMerger(boolean hadMerger) {  
  95.         this.hadMerger = hadMerger;  
  96.     }  
  97.     /** 
  98.      * 当前下载线程的状态 
  99.      */  
  100.     private int completed = 0;  
  101.       
  102.     public synchronized int getCompleted() {  
  103.         return completed;  
  104.     }  
  105.     public synchronized void setCompleted(int completed) {  
  106.         this.completed = completed;  
  107.     }  
  108.     /** 
  109.      * 下载线程个构造方法<br> 
  110.      * 根据已经取得连接的FTPTools得到连接信息<br> 
  111.      * 根据参数取得下载信息 
  112.      * @param rowIndex 
  113.      * @param filepath 
  114.      * @param filesize 
  115.      * @param savepath 
  116.      */  
  117.     public FTPThread(int rowIndex, String filepath, long filesize, String savepath) {  
  118.         super("FTPThread");  
  119.         host = FTPTools.host;  
  120.         port = FTPTools.port;  
  121.         user = FTPTools.user;  
  122.         pswd = FTPTools.pswd;  
  123.         this.rowIndex = rowIndex;  
  124.         this.filepath = filepath;  
  125.         this.filesize = filesize;  
  126.         this.savepath = savepath;  
  127.         ftpThreadLock = new ReentrantLock();  
  128.         setStatus(Constant.THREAD_STATUS_RUNNABLE);  
  129.         start();  
  130.     }  
  131.     public FTPThread(int rowIndex, String filepath, long filesize, String savepath, int status) {  
  132.         super("FTPThread");  
  133.         host = FTPTools.host;  
  134.         port = FTPTools.port;  
  135.         user = FTPTools.user;  
  136.         pswd = FTPTools.pswd;  
  137.         this.rowIndex = rowIndex;  
  138.         this.filepath = filepath;  
  139.         this.filesize = filesize;  
  140.         this.savepath = savepath;  
  141.         ftpThreadLock = new ReentrantLock();  
  142.         setStatus(status);  
  143.         start();  
  144.     }  
  145.     public void run() {  
  146.         getFTPService();  
  147.         getFTPConnect(host, port);  
  148.         if(doLoginFTP(user, pswd)) {  
  149.             ResumeBrokenTransferByThread(this, rowIndex, filepath, filesize, savepath);  
  150.         }  
  151.     }  
  152.     /** 
  153.      * 获取FTPService接口实现类<br> 
  154.      * 首先从配置文件中找<br> 
  155.      * 如果没有则加载默认的实现类 
  156.      * @return 
  157.      * @throws InstantiationException 
  158.      * @throws IllegalAccessException 
  159.      * @throws ClassNotFoundException 
  160.      */  
  161.     public void getFTPService(){  
  162.         try {  
  163.             ftpService = (FTPService)Class.forName(PropertyUtil.getProperty("ftp.service.name", FTPService.FTP_SERVICE_NAME)).newInstance();  
  164.         } catch (Exception e) {  
  165.             e.printStackTrace();  
  166.         }   
  167.     }  
  168.     /** 
  169.      * 根据服务器地址、端口获得ftp链接 
  170.      * @param host 
  171.      * @param port 
  172.      * @return 
  173.      */  
  174.     public String getFTPConnect(String host, int port) {  
  175.         return ftpService.getFTPConnect(host, port);  
  176.     }  
  177.     /** 
  178.      * 执行登录 
  179.      * @param user 
  180.      * @param pswd 
  181.      * @return 
  182.      */  
  183.     public boolean doLoginFTP(String user, String pswd) {  
  184.         return ftpService.doLoginFTP(user, pswd);  
  185.     }  
  186.     /** 
  187.      * 以断点续传的方式下载文件 
  188.      * @param rowIndex 
  189.      * @param filepath 
  190.      * @param filesize 
  191.      * @param savepath 
  192.      */  
  193.     public void ResumeBrokenTransfer(int rowIndex, String filepath, int filesize, String savepath) {  
  194.         ftpService.ResumeBrokenTransfer(rowIndex, filepath, filesize, savepath);  
  195.     }  
  196.     /** 
  197.      * 以多线程、断点续传的方式下载文件 
  198.      * @param rowIndex 
  199.      * @param filepath 
  200.      * @param filesize 
  201.      * @param savepath 
  202.      */  
  203.     public void ResumeBrokenTransferByThread(FTPThread ftpThread, int rowIndex, String filepath, long filesize, String savepath) {  
  204.         ftpService.ResumeBrokenTransferByThread(ftpThread, rowIndex, filepath, filesize, savepath);  
  205.     }  
  206.     /** 
  207.      * 在指定文件路径下查找临时文件合并为一个文件 
  208.      * @param filepath 
  209.      * @param threadCount 
  210.      */  
  211.     public void mergerTempFile(String filepath, int threadCount) {  
  212.         System.out.println("开始合并文件");  
  213.         try {  
  214.             BufferedOutputStream data_output = new BufferedOutputStream(new FileOutputStream(filepath));  
  215.             byte[] temp = new byte[Constant.TEMP_BYTE_LENGTH];  
  216.             for(int i = 0; i < threadCount; i ++) {  
  217.                 File tempFile = new File(filepath + Constant.DOWNLOAD_TEMP_NAME + i);  
  218.                 BufferedInputStream data_input = new BufferedInputStream(new FileInputStream(tempFile));  
  219.                 int read = 0;  
  220.                 int hadRead = 0;  
  221.                 while((read = data_input.read(temp, 0, temp.length)) != -1) {  
  222.                     data_output.write(temp, 0, read);  
  223.                     hadRead += read;  
  224.                 }  
  225.                 data_input.close();  
  226.             }  
  227.             data_output.close();  
  228.         } catch (Exception e) {  
  229.             e.printStackTrace();  
  230.         }   
  231.         setHadMerger(true);  
  232.         System.out.println("合并文件完成");  
  233.         deleteTempFile(filepath, threadCount);  
  234.     }  
  235.     /** 
  236.      * 合并文件完成后删除临时文件 
  237.      * @param filepath 
  238.      * @param threadCount 
  239.      */  
  240.     public void deleteTempFile(String filepath, int threadCount) {  
  241.         if(isHadMerger()) {  
  242.             for(int i = 0; i < threadCount; i ++) {  
  243.                 File tempFile = new File(filepath + Constant.DOWNLOAD_TEMP_NAME + i);  
  244.                 tempFile.delete();  
  245.             }  
  246.         }  
  247.     }  
  248. }  
package com.jfc.ftp.tools; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import com.jfc.ftp.service.FTPService; import com.jfc.ftp.util.Constant; import com.jfc.ftp.util.PropertyUtil; /** * 为断点续传下载文件而启动新的线程 * @author SavageGarden * */ public class FTPThread extends Thread{ /** * 站点URL */ private String host; /** * 站点端口 */ private int port; /** * 用户 */ private String user; /** * 密码 */ private String pswd; /** * 当前线程的FTP操作接口实现类 */ private FTPService ftpService; /** * 第几个下载项 */ private int rowIndex; /** * 要下载的文件路径 */ private String filepath; /** * 要下载的文件大小 */ private long filesize; /** * 要下载的文件保存路径 */ private String savepath; /** * 标记文件已下载量 */ public int hadRead = 0; /** * 下载线程开始时间 */ public long startTime = 0; /** * 下载线程结束时间 */ public long endTime = 0; /** * 当前下载线程的互斥锁 */ public Lock ftpThreadLock; /** * 当前下载线程的状态 */ private int status = Constant.THREAD_STATUS_NEW; public synchronized int getStatus() { return status; } public synchronized void setStatus(int status) { this.status = status; } /** * 是否已经合并文件 */ private boolean hadMerger = false; public synchronized boolean isHadMerger() { return hadMerger; } public synchronized void setHadMerger(boolean hadMerger) { this.hadMerger = hadMerger; } /** * 当前下载线程的状态 */ private int completed = 0; public synchronized int getCompleted() { return completed; } public synchronized void setCompleted(int completed) { this.completed = completed; } /** * 下载线程个构造方法<br> * 根据已经取得连接的FTPTools得到连接信息<br> * 根据参数取得下载信息 * @param rowIndex * @param filepath * @param filesize * @param savepath */ public FTPThread(int rowIndex, String filepath, long filesize, String savepath) { super("FTPThread"); host = FTPTools.host; port = FTPTools.port; user = FTPTools.user; pswd = FTPTools.pswd; this.rowIndex = rowIndex; this.filepath = filepath; this.filesize = filesize; this.savepath = savepath; ftpThreadLock = new ReentrantLock(); setStatus(Constant.THREAD_STATUS_RUNNABLE); start(); } public FTPThread(int rowIndex, String filepath, long filesize, String savepath, int status) { super("FTPThread"); host = FTPTools.host; port = FTPTools.port; user = FTPTools.user; pswd = FTPTools.pswd; this.rowIndex = rowIndex; this.filepath = filepath; this.filesize = filesize; this.savepath = savepath; ftpThreadLock = new ReentrantLock(); setStatus(status); start(); } public void run() { getFTPService(); getFTPConnect(host, port); if(doLoginFTP(user, pswd)) { ResumeBrokenTransferByThread(this, rowIndex, filepath, filesize, savepath); } } /** * 获取FTPService接口实现类<br> * 首先从配置文件中找<br> * 如果没有则加载默认的实现类 * @return * @throws InstantiationException * @throws IllegalAccessException * @throws ClassNotFoundException */ public void getFTPService(){ try { ftpService = (FTPService)Class.forName(PropertyUtil.getProperty("ftp.service.name", FTPService.FTP_SERVICE_NAME)).newInstance(); } catch (Exception e) { e.printStackTrace(); } } /** * 根据服务器地址、端口获得ftp链接 * @param host * @param port * @return */ public String getFTPConnect(String host, int port) { return ftpService.getFTPConnect(host, port); } /** * 执行登录 * @param user * @param pswd * @return */ public boolean doLoginFTP(String user, String pswd) { return ftpService.doLoginFTP(user, pswd); } /** * 以断点续传的方式下载文件 * @param rowIndex * @param filepath * @param filesize * @param savepath */ public void ResumeBrokenTransfer(int rowIndex, String filepath, int filesize, String savepath) { ftpService.ResumeBrokenTransfer(rowIndex, filepath, filesize, savepath); } /** * 以多线程、断点续传的方式下载文件 * @param rowIndex * @param filepath * @param filesize * @param savepath */ public void ResumeBrokenTransferByThread(FTPThread ftpThread, int rowIndex, String filepath, long filesize, String savepath) { ftpService.ResumeBrokenTransferByThread(ftpThread, rowIndex, filepath, filesize, savepath); } /** * 在指定文件路径下查找临时文件合并为一个文件 * @param filepath * @param threadCount */ public void mergerTempFile(String filepath, int threadCount) { System.out.println("开始合并文件"); try { BufferedOutputStream data_output = new BufferedOutputStream(new FileOutputStream(filepath)); byte[] temp = new byte[Constant.TEMP_BYTE_LENGTH]; for(int i = 0; i < threadCount; i ++) { File tempFile = new File(filepath + Constant.DOWNLOAD_TEMP_NAME + i); BufferedInputStream data_input = new BufferedInputStream(new FileInputStream(tempFile)); int read = 0; int hadRead = 0; while((read = data_input.read(temp, 0, temp.length)) != -1) { data_output.write(temp, 0, read); hadRead += read; } data_input.close(); } data_output.close(); } catch (Exception e) { e.printStackTrace(); } setHadMerger(true); System.out.println("合并文件完成"); deleteTempFile(filepath, threadCount); } /** * 合并文件完成后删除临时文件 * @param filepath * @param threadCount */ public void deleteTempFile(String filepath, int threadCount) { if(isHadMerger()) { for(int i = 0; i < threadCount; i ++) { File tempFile = new File(filepath + Constant.DOWNLOAD_TEMP_NAME + i); tempFile.delete(); } } } }

 FTP接口实现类中的ResumeBrokenTransferByThread方法

Java代码
  1. /** 
  2.      * 使用多线程、断点续传方式下载文件<br> 
  3.      * 首先查找要保存的路径下有无缓存文件(以.wfml为后缀)<br> 
  4.      *      不存在 重新开始下载<br> 
  5.      *      存在      继续<br> 
  6.      * 重新开始下载<br> 
  7.      *      读取配置文件要分多少个线程来下载文件<br> 
  8.      *      按照文件大小、线程数量来分配每个线程要下载的文件大小、文件名<br> 
  9.      *      开始断点续传下载<br> 
  10.      * 继续下载<br> 
  11.      *      开始断点续传下载<br> 
  12.      * @param rowIndex 
  13.      * @param filepath 
  14.      * @param filesize 
  15.      * @param savepath 
  16.      */  
  17.     public void ResumeBrokenTransferByThread(final FTPThread ftpThread, final int rowIndex, final String filepath, final long filesize, final String savepath) {  
  18.         final String filename = filepath.substring(filepath.lastIndexOf("/") + 1, filepath.length());  
  19.         final byte[] temp = new byte[Constant.TEMP_BYTE_LENGTH];  
  20.         //得到要创建的线程数量  
  21.         final int threadCount = Integer.parseInt(PropertyUtil.getProperty(Constant.RESUME_THREAD_COUNT_PROPNAME, Constant.RESUME_THREAD_COUNT_DEFAULT));  
  22.         final String[] downloadSizeArray = SystemTools.getDownloadSizeArray(filesize, threadCount);  
  23.         for(int i = 0; i < threadCount; i ++) {  
  24.             File temp_file = new File(savepath + File.separator + filename + Constant.DOWNLOAD_TEMP_NAME + i);  
  25.             System.out.println("文件" + i + "大小为:" + temp_file.length());  
  26.             ftpThread.hadRead += temp_file.length();  
  27.               
  28.         }  
  29.         System.out.println("ftpThread.hadRead : " + ftpThread.hadRead);  
  30.         for(int i = 0; i < threadCount; i ++) {  
  31.             final int index = i;  
  32.             Thread resumeThread = new Thread(){  
  33.                 //当前线程的缓存文件  
  34.                 File tempFile = new File(savepath + File.separator + filename + Constant.DOWNLOAD_TEMP_NAME + index);  
  35.                 public void run() {  
  36.                     SocketFTPService socketFTPService = new SocketFTPService();  
  37.                     socketFTPService.getFTPConnect(host, port);  
  38.                     if(socketFTPService.doLoginFTP(user, pswd)) {  
  39.                         try {  
  40.                             int read = 0;  
  41.                             int hadRead = 0;  
  42.                             //当前线程要下载的文件大小  
  43.                             String downsize = downloadSizeArray[index].split(":")[1];  
  44.                             //当前线程要下载的文件起始点  
  45.                             String skipsize = downloadSizeArray[index].split(":")[0];  
  46.                             //将hadRead(已读文件大小)置为缓存文件的大小  
  47.                             hadRead = (int)tempFile.length();  
  48.                             //设定文件指针(下载位置)  
  49.                             //socketFTPService.doFTPCommand("REST " + (Integer.parseInt(skipsize) + temp_file.length()));  
  50.                             //readRespond();  
  51.                             socketFTPService.dataSocket = socketFTPService.getDataSocket();  
  52.                             socketFTPService.doFTPCommand("RETR " + filepath);  
  53.                             BufferedInputStream data_input = new BufferedInputStream(socketFTPService.dataSocket.getInputStream());  
  54.                               
  55.                             RandomAccessFile raf = new RandomAccessFile(tempFile, "rw");  
  56.                             //跳过当前线程要下载的文件起始点和缓存文件大小之和  
  57.                             data_input.skip(Integer.parseInt(skipsize) + hadRead);  
  58.                             raf.seek(hadRead);  
  59.                               
  60.                             System.out.println("线程" + index + "已下载 " + hadRead);  
  61.                             if(ftpThread.startTime == 0) {  
  62.                                 ftpThread.startTime = System.currentTimeMillis();  
  63.                             }  
  64.                             SystemTools.addObserver();  
  65.                               
  66.                             while(hadRead < Integer.parseInt(downsize)) {  
  67.                                 if(ftpThread.getStatus() == Constant.THREAD_STATUS_RUNNABLE) {  
  68.                                     while((read = data_input.read(temp, 0, temp.length)) != -1) {  
  69.                                         int temp_hadRead = hadRead;  
  70.                                         if((temp_hadRead += read) > Integer.parseInt(downsize)) {  
  71.                                             read = Integer.parseInt(downsize) - hadRead;  
  72.                                         }  
  73.                                         raf.write(temp, 0, read);  
  74.                                         hadRead += read;  
  75.                                           
  76.                                         ftpThread.ftpThreadLock.lock();  
  77.                                         try {  
  78.                                             ftpThread.hadRead += read;  
  79.                                         } finally {  
  80.                                             ftpThread.ftpThreadLock.unlock();  
  81.                                         }  
  82.                                         SystemTools.getCurrentSpeed(rowIndex, ftpThread.startTime, ftpThread.hadRead);  
  83.                                         SystemTools.getPrice(rowIndex, ftpThread.hadRead, filesize);  
  84.                                         SwingUtilities.invokeLater(SystemTools.updateProgressBarRunnable);  
  85.                                     }  
  86.                                     System.out.println("第" + index + "个线程完成下载" + hadRead + ",完成下载" + ftpThread.hadRead);  
  87.                                     raf.close();  
  88.                                     if(hadRead == tempFile.length()) {  
  89.                                         ftpThread.setCompleted(ftpThread.getCompleted() + 1);  
  90.                                         System.out.println(ftpThread.getCompleted());  
  91.                                     }  
  92.                                     if(ftpThread.getCompleted() == threadCount && ftpThread.hadRead == filesize) {  
  93.                                         ftpThread.endTime = System.currentTimeMillis();  
  94.                                         SystemTools.getFinalSpeed(rowIndex, ftpThread.startTime, ftpThread.endTime, filesize);  
  95.                                         ftpThread.mergerTempFile(savepath + File.separator + filename, threadCount);  
  96.                                     }  
  97.                                 }  
  98.                             }  
  99.                         } catch(Exception e) {  
  100.                             e.printStackTrace();  
  101.                         }  
  102.                     }  
  103.                 }  
  104.             };  
  105.             resumeThread.start();  
  106.         }  
  107.     } 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值