[工具库]JFileDownloader工具类——多线程下载网络文件,并保存在本地

本人大四即将毕业的准程序员(JavaSE、JavaEE、android等)一枚,小项目也做过一点,于是乎一时兴起就写了一些工具。

我会在本博客中陆续发布一些平时可能会用到的工具。

代码质量可能不是很好,大家多担待!

代码或者思路有不妥之处,还希望大牛们能不吝赐教哈!

 

以下代码为本人原创,转载请注明:

本文转载,来自:http://www.cnblogs.com/tiantianbyconan/archive/2013/02/20/2919132.html

 

JFileDownloader:用于多线程下载网络文件,并保存在本地。

源码如下:

1.JFileDownloader类:主要负责下载的初始化可启动工作。

View Code
  1 package com.wangjie.extrautil.jfiledownloader;
  2 
  3 import java.io.File;
  4 import java.net.HttpURLConnection;
  5 import java.net.URL;
  6 
  7 /**
  8  * 
  9  * @author wangjie
 10  * @version 创建时间:2013-2-7 下午1:40:52
 11  */
 12 public class JFileDownloader{
 13     private String urlPath;
 14     private String destFilePath;
 15     private int threadCount;
 16     private JFileDownloadThread[] threads;
 17     
 18     private JFileDownloadListener fileDownloadListener; // 进度监听器
 19     private JFileDownloaderNotificationThread notificationThread; // 通知进度线程
 20     
 21     private File destFile;
 22     /**
 23      * 下载过程中文件的后缀名。
 24      */
 25     public final static String DOWNLOADING_SUFFIX = ".jd"; 
 26     /**
 27      * 默认使用的线程数量。<br>
 28      * 如果不设置线程数量参数(threadCount),则默认线程启动数量为1,即单线程下载。
 29      */
 30     public static final int DEFAULT_THREADCOUNT = 1;
 31     /**
 32      * 生成JFileDownloader对象。
 33      * @param urlPath 要下载的目标文件URL路径
 34      * @param destFilePath 要保存的文件目标(路径+文件名)
 35      * @param threadCount 下载该文件所需要的线程数量
 36      */
 37     public JFileDownloader(String urlPath, String destFilePath, int threadCount) {
 38         this.urlPath = urlPath;
 39         this.destFilePath = destFilePath;
 40         this.threadCount = threadCount;
 41     }
 42     /**
 43      * 生成JFileDownloader对象,其中下载线程数量默认是1,也就是选择单线程下载。
 44      * @param urlPath urlPath 要下载的目标文件URL路径
 45      * @param destFilePath destFilePath 要保存的文件目标(路径+文件名)
 46      */
 47     public JFileDownloader(String urlPath, String destFilePath) {
 48         this(urlPath, destFilePath, DEFAULT_THREADCOUNT);
 49     }
 50     /**
 51      * 默认的构造方法,使用构造方法后必须要调用set方法来设置url等下载所需配置。
 52      */
 53     public JFileDownloader() {
 54         
 55     }
 56     /**
 57      * 开始下载方法(流程分为3步)。
 58      * <br><ul>
 59      * <li>检验URL的合法性<br>
 60      * <li>计算下载所需的线程数量和每个线程需下载多少大小的文件<br>
 61      * <li>启动各线程。
 62      * </ul>
 63      * @author wangjie
 64      * @throws Exception 如果设置的URL,includes等参数不合法,则抛出该异常
 65      */
 66     public void startDownload() throws Exception{
 67         checkSettingfValidity(); // 检验参数合法性
 68         
 69         URL url = new URL(urlPath);
 70         HttpURLConnection conn = (HttpURLConnection)url.openConnection();
 71         conn.setConnectTimeout(20 * 1000);
 72         // 获取文件长度
 73         long size = conn.getContentLength();
 74 //        int size = conn.getInputStream().available();
 75         if(size < 0 || null == conn.getInputStream()){
 76             throw new Exception("网络连接错误,请检查URL地址是否正确");
 77         }
 78         conn.disconnect();
 79         
 80         
 81         // 计算每个线程需要下载多少byte的文件
 82         long perSize = size % threadCount == 0 ? size / threadCount : (size / threadCount + 1);
 83         // 建立目标文件(文件以.jd结尾)
 84         destFile = new File(destFilePath + DOWNLOADING_SUFFIX);
 85         destFile.createNewFile();
 86         
 87         threads = new JFileDownloadThread[threadCount];
 88         
 89         // 启动进度通知线程
 90         notificationThread = new JFileDownloaderNotificationThread(threads, fileDownloadListener, destFile, size);
 91         notificationThread.start();
 92         
 93         // 初始化若干个下载线程
 94         for(int i = 0; i < threadCount; i++){
 95             if(i != (threadCount - 1)){
 96                 threads[i] = new JFileDownloadThread(urlPath, destFile, 
 97                         i * perSize, perSize, notificationThread);
 98             }else{
 99                 threads[i] = new JFileDownloadThread(urlPath, destFile, 
100                         i * perSize, size - (threadCount - 1) * perSize, notificationThread);
101             }
102             threads[i].setPriority(8);
103 //            threads[i].start();
104         }
105         // 启动若干个下载线程(因为下载线程JFileDownloaderNotificationThread中使用了threads属性,所以必须等下载线程全部初始化以后才能启动线程)
106         for(JFileDownloadThread thread : threads){
107             thread.start();
108         }
109         
110     }
111     /**
112      * 取消所有下载线程。
113      * @author wangjie
114      */
115     public void cancelDownload(){
116         if(null != threads && 0 != threads.length && null != notificationThread){
117             for(JFileDownloadThread thread : threads){ // 终止所有下载线程
118                 thread.cancelThread();
119             }
120             notificationThread.cancelThread(); // 终止通知线程
121             System.out.println("下载已被终止。");
122             return;
123         }
124         System.out.println("下载线程还未启动,无法终止。");
125     }
126     
127     /**
128      * 设置要下载的目标文件URL路径。
129      * @author wangjie
130      * @param urlPath 要下载的目标文件URL路径
131      * @return 返回当前JFileDownloader对象
132      */
133     public JFileDownloader setUrlPath(String urlPath) {
134         this.urlPath = urlPath;
135         return this;
136     }
137     /**
138      * 设置要保存的目标文件(路径+文件名)。
139      * @author wangjie
140      * @param destFilePath 要保存的文件目标(路径+文件名)
141      * @return 返回当前JFileDownloader对象
142      */
143     public JFileDownloader setDestFilePath(String destFilePath) {
144         this.destFilePath = destFilePath;
145         return this;
146     }
147     /**
148      * 设置下载该文件所需要的线程数量。
149      * @author wangjie
150      * @param threadCount 下载该文件所需要的线程数量
151      * @return 返回当前JFileDownloader对象
152      */
153     public JFileDownloader setThreadCount(int threadCount) {
154         this.threadCount = threadCount;
155         return this;
156     }
157     
158     //观察者模式来获取下载进度
159     /**
160      * 设置监听器,以获取下载进度。
161      */
162     public JFileDownloader setFileDownloadListener(
163             JFileDownloadListener fileDownloadListener) {
164         this.fileDownloadListener = fileDownloadListener;
165         return this;
166     }
167     /**
168      * 通过该方法移出相应的监听器对象。
169      * @author wangjie
170      * @param fileDownloadListener 要移除的监听器对象
171      */
172     public void removeFileDownloadListener(
173             JFileDownloadListener fileDownloadListener) {
174         fileDownloadListener = null;
175     }
176 
177 
178     /**
179      * 检验设置的参数是否合法。
180      * @author wangjie
181      * @throws Exception 目标文件URL路径不合法,或者线程数小于1,则抛出该异常
182      */
183     private void checkSettingfValidity() throws Exception{
184         if(null == urlPath || "".equals(urlPath)){
185             throw new Exception("目标文件URL路径不能为空");
186         }
187         if(threadCount < 1){
188             throw new Exception("线程数不能小于1");
189         }
190     }
191     
192 }

 

2.JFileDownloadListener接口:该接口用于监听JFileDownloader下载的进度。

View Code
 1 package com.wangjie.extrautil.jfiledownloader;
 2 
 3 import java.io.File;
 4 
 5 /**
 6  * 
 7  * 该接口用于监听JFileDownloader下载的进度。
 8  * 
 9  * @author wangjie
10  * @version 创建时间:2013-2-7 下午2:12:45
11  */
12 public interface JFileDownloadListener {
13     /**
14      * 该方法可获得文件的下载进度信息。
15      * @author wangjie
16      * @param progress 文件下载的进度值,范围(0-100)。0表示文件还未开始下载;100则表示文件下载完成。
17      * @param speed 此时下载瞬时速度(单位:kb/每秒)。
18      * @param remainTime 此时剩余下载所需时间(单位为毫秒)。
19      */
20     public void downloadProgress(int progress, double speed, long remainTime);
21     /**
22      * 文件下载完成会调用该方法。
23      * @author wangjie
24      * @param file 返回下载完成的File对象。
25      * @param downloadTime 下载所用的总时间(单位为毫秒)。
26      */
27     public void downloadCompleted(File file, long downloadTime);
28 }

 

3.JFileDownloaderNotificationThread类:该线程为通知下载进度的线程。

View Code
  1 package com.wangjie.extrautil.jfiledownloader;
  2 
  3 import java.io.File;
  4 import java.math.BigDecimal;
  5 
  6 /**
  7  * 该线程为通知下载进度的线程。
  8  * 用于在下载未完成时通知用户下载的进度,范围(0-100)。0表示文件还未开始下载;100则表示文件下载完成。
  9  * 此时下载瞬时速度(单位:kb/每秒)。
 10  * 在完成时返回下载完成的File对象给用户。返回下载所用的总时间(单位为毫秒)给用户。
 11  * @author wangjie
 12  * @version 创建时间:2013-2-17 下午12:23:59
 13  */
 14 public class JFileDownloaderNotificationThread extends Thread{
 15     private JFileDownloadThread[] threads;
 16     private JFileDownloadListener fileDownloadListener;
 17     private File destFile;
 18     private long destFileSize;
 19     private boolean isRunning; // 线程运行停止标志
 20     private boolean notificationTag; // 通知标志
 21     /**
 22      * 通过该方法构建一个进度通知线程。
 23      * @param threads 下载某文件需要的所有线程。
 24      * @param fileDownloadListener 要通知进度的监听器对象。
 25      * @param destFile 下载的文件对象。
 26      */
 27     public JFileDownloaderNotificationThread(JFileDownloadThread[] threads,
 28             JFileDownloadListener fileDownloadListener, File destFile, long destFileSize) {
 29         this.threads = threads;
 30         this.fileDownloadListener = fileDownloadListener;
 31         this.destFile = destFile;
 32         this.destFileSize = destFileSize;
 33     }
 34 
 35     /**
 36      * 不断地循环来就检查更新进度。
 37      */
 38     @Override
 39     public void run() {
 40         isRunning = true;
 41         long startTime = 0;
 42         if(null != fileDownloadListener){
 43             startTime = System.currentTimeMillis(); // 文件下载开始时间
 44         }
 45         
 46         long oldTemp = 0; // 上次已下载数据长度
 47         long oldTime = 0; // 上次下载的当前时间
 48         
 49         while(isRunning){
 50             if(notificationTag){ // 如果此时正等待检查更新进度。
 51                 // 计算此时的所有线程下载长度的总和
 52                 long temp = 0;
 53                 for(JFileDownloadThread thread : threads){
 54                     temp += thread.currentLength;
 55                 }
 56 //                System.out.println("temp: " + temp);
 57 //                System.out.println("destFileSize: " + destFileSize);
 58                 // 换算成进度
 59                 int progress = (int) ((double)temp * 100 / (double)destFileSize);
 60                 
 61                 // 把进度通知给监听器
 62                 if(null != fileDownloadListener){
 63                     // 计算瞬时速度
 64                     long detaTemp = temp - oldTemp; // 两次更新进度的时间段内的已下载数据差
 65                     long detaTime = System.currentTimeMillis() - oldTime; // 两次更新进度的时间段内的时间差 
 66                     // 两次更新进度的时间段内的速度作为瞬时速度
 67                     double speed = ((double)detaTemp / 1024) / ((double)(detaTime) / 1000); 
 68                     
 69                     // 保留小数点后2位,最后一位四舍五入
 70                     speed = new BigDecimal(speed).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
 71                     
 72                     // 计算剩余下载时间
 73                     double remainTime = (double)(destFileSize - temp) / speed;
 74                     if(Double.isInfinite(remainTime) || Double.isNaN(remainTime)){
 75                         remainTime = 0;
 76                     }else{
 77                         remainTime = new BigDecimal(remainTime).setScale(0, BigDecimal.ROUND_HALF_UP).longValue();
 78                     }
 79                     
 80                     // 通知监听者进度和速度以及下载剩余时间
 81                     fileDownloadListener.downloadProgress(progress, speed, (long)remainTime);
 82                     
 83                     // 重置上次已下载数据长度和上次下载的当前时间
 84                     oldTemp = temp;
 85                     oldTime = System.currentTimeMillis();
 86                 }
 87                 
 88                 // 如果下载进度达到100,则表示下载完毕
 89                 if(100 <= progress){
 90                     // 给下载好的文件进行重命名,即去掉DOWNLOADING_SUFFIX后缀
 91                     String oldPath = destFile.getPath();
 92                     File newFile = new File(oldPath.substring(0, oldPath.lastIndexOf(".")));
 93                     // 检查去掉后的文件是否存在。如果存在,则删除原来的文件并重命名下载的文件(即:覆盖原文件)
 94                     if(newFile.exists()){
 95                         newFile.delete();
 96                     }
 97                     System.out.println(destFile.renameTo(newFile));// 重命名
 98                     // 通知监听器,并传入新的文件对象
 99                     if(null != fileDownloadListener){
100                         fileDownloadListener.downloadCompleted(newFile, System.currentTimeMillis() - startTime);
101                     }
102                     isRunning = false; // 文件下载完就结束通知线程。
103                 }
104                 notificationTag = false;
105             }
106             // 设置为每100毫秒进行检查并更新通知
107             try {
108                 Thread.sleep(100);
109             } catch (InterruptedException e) {
110                 e.printStackTrace();
111             }
112             
113         }
114         
115     }
116     /**
117      * 调用这个方法,则会使得线程处于待检查更新进度状态。
118      * @author wangjie
119      */
120     public synchronized void notificationProgress(){
121         notificationTag = true;
122     }
123     /**
124      * 取消该通知线程
125      * @author wangjie
126      */
127     public void cancelThread(){
128         isRunning = false;
129     }
130     
131 
132 }

 

4.JFileDownloadThread类:真正的下载线程,该线程用于执行该线程所要负责下载的数据。

View Code
  1 package com.wangjie.extrautil.jfiledownloader;
  2 
  3 import java.io.File;
  4 import java.io.IOException;
  5 import java.io.InputStream;
  6 import java.io.RandomAccessFile;
  7 import java.net.HttpURLConnection;
  8 import java.net.URL;
  9 
 10 /**
 11  * 
 12  * 真正的下载线程,该线程用于执行该线程所要负责下载的数据。
 13  * 
 14  * @author wangjie
 15  * @version 创建时间:2013-2-7 上午11:58:24
 16  */
 17 public class JFileDownloadThread extends Thread{
 18     private String urlPath;
 19     private File destFile;
 20     private long startPos;
 21     /**
 22      * 此线程需要下载的数据长度。
 23      */
 24     public long length;
 25     /**
 26      * 此线程现在已下载好了的数据长度。
 27      */
 28     public long currentLength;
 29     
 30     private JFileDownloaderNotificationThread notificationThread;
 31     private boolean isRunning = true;
 32     
 33     /**
 34      * 构造方法,可生成配置完整的JFileDownloadThread对象
 35      * @param urlPath 要下载的目标文件URL
 36      * @param destFile 要保存的目标文件
 37      * @param startPos 该线程需要下载目标文件第几个byte之后的数据
 38      * @param length 该线程需要下载多少长度的数据
 39      * @param notificationThread 通知进度线程
 40      */
 41     public JFileDownloadThread(String urlPath, File destFile, long startPos,
 42             long length, JFileDownloaderNotificationThread notificationThread) {
 43         this.urlPath = urlPath;
 44         this.destFile = destFile;
 45         this.startPos = startPos;
 46         this.length = length;
 47         this.notificationThread = notificationThread;
 48     }
 49     /**
 50      * 该方法将执行下载功能,并把数据存储在目标文件中的相应位置。
 51      */
 52     @Override
 53     public void run() {
 54         RandomAccessFile raf = null;
 55         HttpURLConnection conn = null;
 56         InputStream is = null;
 57         try {
 58 //            URL url = new URL("http://localhost:8080/firstserver/files/hibernate.zip");
 59             URL url = new URL(urlPath);
 60             conn = (HttpURLConnection)url.openConnection();
 61             conn.setConnectTimeout(20 * 1000);
 62             is = conn.getInputStream();
 63             raf = new RandomAccessFile(destFile, "rw");
 64             raf.setLength(conn.getContentLength()); // 设置保存文件的大小
 65 //            raf.setLength(conn.getInputStream().available());
 66             
 67             // 设置读入和写入的文件位置
 68             is.skip(startPos);
 69             raf.seek(startPos);
 70             
 71             currentLength = 0; // 当前已下载好的文件长度
 72             byte[] buffer = new byte[1024 * 1024];
 73             int len = 0;
 74             while(currentLength < length && -1 != (len = is.read(buffer))){
 75                 if(!isRunning){
 76                     break;
 77                 }
 78                 if(currentLength + len > length){
 79                     raf.write(buffer, 0, (int)(length - currentLength));
 80                     currentLength = length;
 81                     notificationThread.notificationProgress(); // 通知进度线程来更新进度
 82                     return;
 83                 }else{
 84                     raf.write(buffer, 0, len);
 85                     currentLength += len;
 86                     notificationThread.notificationProgress(); // 通知进度线程来更新进度
 87                 }
 88             }
 89         } catch (Exception e) {
 90             e.printStackTrace();
 91         } finally{
 92             try {
 93                 is.close();
 94                 raf.close();
 95                 conn.disconnect();
 96             } catch (IOException e) {
 97                 e.printStackTrace();
 98             }
 99         }
100         
101     }
102     /**
103      * 取消该线程下载
104      * @author wangjie
105      */
106     public void cancelThread(){
107         isRunning = false;
108     }
109     
110     
111 }

 

使用方法如下:

 1 String urlPath = "http://localhost:8080/firstserver/files/test.zip";
 2 String destFilePath = "C:\\Users\\admin\\Desktop\\杂\\临时仓库\\test.zip";
 3 int threadCount = 3;
 4 
 5 JFileDownloader downloader = new JFileDownloader(urlPath, destFilePath, threadCount);
 6 //或者:
 7 JFileDownloader downloader = new JFileDownloader()
 8             .setUrlPath(urlPath)
 9             .setDestFilePath(destFilePath)
10             .setThreadCount(threadCount)
11             .setFileDownloadListener(new JFileDownloadListener() { // 设置进度监听器
12                     public void downloadProgress(int progress, double speed, long remainTime) {
13                         System.out.println("文件已下载:" + progress + "%,下载速度为:" + speed + "kb/s,剩余所需时间:" + remainTime + "毫秒");
14                     }
15                     public void downloadCompleted(File file, long downloadTime) {
16                         System.out.println("文件:" + file.getName() + "下载完成,用时:" + downloadTime + "毫秒");
17                     }
18             });
19     try {
20         downloader.startDownload(); // 开始下载
21     } catch (Exception e) {
22         e.printStackTrace();
23     }

 

 

转载于:https://www.cnblogs.com/tiantianbyconan/archive/2013/02/20/2919132.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/360969.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用Bean验证扩展PrimeFaces CSV

你们中有些人已经知道我和我的合著者Mertalışkan正在研究PrimeFaces Cookbook的2.版。 Packt Publishing允许我从新章节“客户端验证”的一个食谱中摘录一小部分摘录。 这将有助于使读者知道这本书的内容。 在此博客文章中&#xff0c;我想讨论使用Bean验证扩展的PrimeFaces客…

ASP.NET.CORE发布后启动网站出现500.19-0x8007000d错误解决方法

本项目使用的是netcoreapp2.2&#xff0c;缺少的XML文件是swagger。发布采用的是文件系统、依赖框架。 我第一次发布asp.net.core的后台&#xff0c;发布后启动网站出现500.19错误-0x8007000d。百度查了一下原因&#xff0c;2其中大多数人说是因为没有权限&#xff0c;需要编辑…

WildFly和Docker上的Java EE 7动手实验室

Java EE 7动手实验室已在全球范围内交付&#xff0c;它是一个非常标准的应用程序&#xff0c;显示了典型Java EE 7应用程序的设计模式和反模式。 它显示了如何在接近现实的应用程序中使用以下技术&#xff1a; WebSocket 1.0 JSON处理1.0 批次1.0 上下文和依赖注入1.1 Jav…

一罐将其全部统治:Arquillian + Java 8

借助Java 8 &#xff0c;已实现了许多新的语言改进&#xff0c;以简化开发人员的生活。 在我看来&#xff0c; Java 8的最大优点之一是&#xff0c;在某些情况下&#xff0c;已开发的代码看起来比使用以前的方法更漂亮&#xff0c;我指的是Lambdas和Method引用。 这篇文章不是要…

uni-app引入阿里巴巴矢量库图标后,顶部导航栏显示小方块

引入阿里巴巴矢量图标库 首先在阿里巴巴创建项目&#xff0c;拥有图标 具体引入方法参考&#xff1a; [https://blog.csdn.net/Dream_Weave/article/details/88550978?depth_1-utm_sourcedistribute.pc_relevant.none-task&utm_sourcedistribute.pc_relevant.none-task]在…

使用Spring Integration进行消息处理

Spring Integration提供了Spring框架的扩展&#xff0c;以支持著名的企业集成模式。 它在基于Spring的应用程序中启用轻量级消息传递&#xff0c;并支持与外部系统的集成。 Spring Integration的最重要目标之一是为构建可维护且可测试的企业集成解决方案提供一个简单的模型。 …

使用RxNetty访问Meetup的流API

本文将涉及多个主题&#xff1a;响应式编程&#xff0c;HTTP&#xff0c;解析JSON以及与社交API集成。 完全在一个用例中&#xff1a;我们将通过非夸张的RxNetty库实时加载和处理新的metup.com事件&#xff0c;结合Netty框架的强大功能和RxJava库的灵活性。 Meetup提供了公开可…

js、react对象名和对象属性赋值

const resValue {}; resValue[standards${standardsNumber}] ""; Console.log(:test"&#xff0c;resValue )//

TIBCO产品的微服务和DevOps

如今&#xff0c;每个人都在谈论微服务。 您可以在数百篇文章和博客文章中读到很多有关微服务的信息。 马丁福勒 &#xff08; Martin Fowler &#xff09;的文章是一个很好的起点&#xff0c;该文章引发了有关这种新架构概念的大量讨论。 另一个很棒的资源是独立于供应商的分…

使用Degraph管理软件包依赖关系

软件开发领域的很大一部分是使系统的复杂性尽可能地低。 但是复杂性到底是什么&#xff1f; 虽然确切的语义有很大不同&#xff0c;但取决于您询问的人&#xff0c;大多数人可能都认为这与系统中部件的数量及其交互有很大关系。 考虑太空中的大理石&#xff0c;即行星&#xf…

[转载] 应急管理体系及其业务流程研究

转载于:https://www.cnblogs.com/6DAN_HUST/archive/2013/03/04/2942337.html

WP8手机上的图标

一直不清楚WP8手机上两个圆的标志是什么意思&#xff0c;今天看到下面的链接&#xff0c;终于搞明白了&#xff0c;原来是打开了GPS就有。 http://www.windowsphone.com/en-us/how-to/wp8/basics/what-do-the-icons-on-my-phone-mean 转载于:https://www.cnblogs.com/wonderow/…

ASIHTTPRequest类库简介和使用说明

官方网站&#xff1a; http://allseeing-i.com/ASIHTTPRequest/ 。可以从上面下载到最新源码&#xff0c;以及获取到相关的资料。 使用iOS SDK中的HTTP网络请求API&#xff0c;相当的复杂&#xff0c;调用很繁琐&#xff0c;ASIHTTPRequest就是一个对CFNetwork API进行了封装&a…

UltraESB的首选IDE – IntelliJ IDEA

在AdroitLogic&#xff0c;我们长期以来一直在使用IntelliJ IDEA进行开发。 它是Java和相关语言/技术的最佳IDE&#xff08;它可能也是许多其他语言的选择&#xff0c;但我的经验主要是Java和相关技术&#xff09;。 Groovy和IDEA的Grails的集成很棒。 通过自动发现JDBC驱动程…

跟我一步一步开发自己的Openfire插件

这篇是简单插件开发&#xff0c;下篇聊天记录插件。 开发环境&#xff1a; System&#xff1a;Windows WebBrowser&#xff1a;IE6、Firefox3 JavaEE Server&#xff1a;tomcat5.0.2.8、tomcat6 IDE&#xff1a;eclipse、MyEclipse 8开发依赖库&#xff1a; Jdk1.6、jasper-com…

Apache FOP与Eclipse和OSGi的集成

Apache FOP是由XSL格式化对象&#xff08; XSL-FO &#xff09;驱动的开源打印处理器。 例如&#xff0c;将数据对象转换为PDF可能非常有用。 但是&#xff0c;将其集成到PDE中并最终以OSGi Service的形式运行并最终显得有些麻烦。 因此&#xff0c;我提供了一个P2存储库&…

不删除侦听器–使用ListenerHandles

听一个可观察的实例并对它的变化做出反应很有趣。 做一些必要的事情来打断或结束这种聆听会变得很有趣。 让我们看看问题的根源和解决方法。 总览 这篇文章将首先讨论这种情况&#xff0c;然后再讨论常见的方法和问题所在。 然后&#xff0c;它将提供解决大多数问题的简单抽象…

使用Google Guava Cache进行本地缓存

很多时候&#xff0c;我们将不得不从数据库或另一个Web服务获取数据或从文件系统加载数据。 在涉及网络呼叫的情况下&#xff0c;将存在固有的网络等待时间&#xff0c;网络带宽限制。 解决此问题的方法之一是在应用程序本地拥有一个缓存。 如果您的应用程序跨越多个节点&…

JAX-RS 2.0:服务器端处理管道

这篇文章的灵感来自JAX-RS 2.0规范文档 &#xff08;附录C&#xff09;中的Processing Pipeline部分。 我喜欢它是因为它提供了JAX-RS中所有模块的漂亮快照-以准备好吞咽的胶囊形式&#xff01; 礼貌– JAX-RS 2.0规范文档 因此&#xff0c;我想到了使用此图简要概述不同的JA…

基于TCP/IP的文件服务器编程一例

来源&#xff0c;华清远见嵌入式学院实验手册&#xff0c;代码来源&#xff1a;华清远见曾宏安 实现的功能&#xff1a; 编写TCP文件服务器和客户端。客户端可以上传和下载文件 客户端支持功能如下&#xff1a; 1.支持一下命令 help 显示客户端所有命令和说明 list 显示服务器…