声明:代码为核心代码,为了简洁!完整代码请移步我的github -->【带我传送到github】
UReport2默认提供的名为“服务器文件系统”的报表存储机制,实际上是实现了UReport2提供的com.bstek.ureport.provider.report.ReportProvider接口;如果我们定义了自己的报表存储器,只需要实现了ReportProvider接口后,并将实现类配置到Spring中,让其成为一个标准的Spring Bean,这样UReport2就会检测到它而将其加载。
如果你不想将报表文件存储到默认的路径下,而是FTP文件服务器中,那么下面的代码可以解决你得问题!
技术选型:
1、项目FTP工具包使用 apache 的 common-net
2、项目实现了FTPClient连接池
首先:
你得有个FTP服务器(没弄好的话,得百度先搭建好)。
代码部分:
pom.xml
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.3</version>
</dependency>
<dependency>
<groupId>commons-pool</groupId>
<artifactId>commons-pool</artifactId>
</dependency>
<!-- 消除冗余代码,可以省去get、set、构造器... 方法 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
application.yml:
# FTP 配置
ftp:
factory:
hostname: 192.168.56.1
port: 21
username: 你得名称
password: 你得密码
client-timeout: 10000
encoding: utf8
# retry-times:
# passive-mode:
# file-type:
# rename-uploaded: true
# ureport FTP 存储
ureport:
ftp:
provider:
prefix: ftp-
disabled: false
basePath: ureport_file\
FTPClient 工厂:
package indi.qiaolin.test.ureport.ftp;
import java.io.IOException;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPClientConfig;
import org.apache.commons.net.ftp.FTPConnectionClosedException;
import org.apache.commons.net.ftp.FTPReply;
import org.apache.commons.pool.PoolableObjectFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import indi.qiaolin.test.ureport.exception.ConnectionPoolException;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
/**
* FTPClient 工厂类, 提供FTPClient实例的创建、销毁、验证工作
* @author qiaolin
* @version 2018年5月9日
*
*/
@Data
@Slf4j
@Component
@ConfigurationProperties("ftp.factory")
public class FTPClientFactory implements PoolableObjectFactory<FTPClient>{
private String hostname; // 地址
private int port; // 端口
private String username; // 用户名
private String password; // 密码
private boolean passiveMode; // 被动模式
private String encoding = "UTF-8"; // 文件编码
private int clientTimeout; // 客户端超时毫秒数
private int threadNum; // 线程数
private int fileType = 2; // 文件上传形式,默认二进制; 参考FTP类中常量
private boolean renameUploaded;
private int retryTimes;
/**
* 创建一个FTPClient对象
*/
@Override
public FTPClient makeObject() throws Exception {
FTPClient ftpClient = new FTPClient();
ftpClient.setConnectTimeout(clientTimeout);
ftpClient.connect(hostname, port);
int replyCode = ftpClient.getReplyCode();
if(!FTPReply.isPositiveCompletion(replyCode)){
ftpClient.disconnect();
log.warn("FTPServer 拒绝连接 !");
return null;
}
boolean login = ftpClient.login(username, password);
if(!login){
throw new FTPConnectionClosedException("FTPServer 登录失败!");
}
ftpClient.setControlEncoding("UTF-8");
FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_NT);
conf.setServerLanguageCode("zh");
ftpClient.configure(conf);
//ftpClient.setFileType(fileType);
ftpClient.setBufferSize(1024);
if (passiveMode) {
ftpClient.enterLocalPassiveMode();
}
return ftpClient;
}
/**
* 销毁一个FTPClient对象
*/
@Override
public void destroyObject(FTPClient ftpClient) throws Exception {
try {
if(ftpClient != null && ftpClient.isConnected()){
ftpClient.logout();
}
} catch (Exception e) {
e.printStackTrace();
}finally{
ftpClient.disconnect();
}
}
/**
* 验证一个FTPClient对象是否可用
*/
@Override
public boolean validateObject(FTPClient ftpClient) {
try {
return ftpClient.sendNoOp();
} catch (IOException e) {
e.printStackTrace();
throw new ConnectionPoolException("验证FTPClient 对象失败!");
}
}
@Override
public void activateObject(FTPClient obj) throws Exception {
}
@Override
public void passivateObject(FTPClient obj) throws Exception {
}
}
FTP 客户端连接池:
package indi.qiaolin.test.ureport.ftp;
import java.util.NoSuchElementException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import javax.annotation.PreDestroy;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.pool.ObjectPool;
import org.apache.commons.pool.PoolableObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import indi.qiaolin.test.ureport.exception.ConnectionPoolException;
/**
* FTP 客户端连接池
* @author qiaolin
* @version 2018年5月9日
*
*/
@Component
public class FTPClientPool implements ObjectPool<FTPClient>{
private FTPClientFactory factory;
private BlockingQueue<FTPClient> pool;
@Autowired
public FTPClientPool(FTPClientFactory factory) {
this(10, factory);
}
public FTPClientPool(int maxPoolSize, FTPClientFactory factory){
this.factory = factory;
this.pool = new ArrayBlockingQueue<>(maxPoolSize * 2);
try {
for(int i = 0; i < maxPoolSize; i++){
addObject();
}
} catch (Exception e) {
e.printStackTrace();
throw new ConnectionPoolException("FTP 连接池创建失败!");
}
}
/**
* 添加对象到连接池
*/
@Override
public void addObject() throws Exception, IllegalStateException, UnsupportedOperationException {
pool.offer(factory.makeObject(), 3, TimeUnit.SECONDS );
}
/**
* 从连接池中取出一个对象
*/
@Override
public FTPClient borrowObject() throws Exception, NoSuchElementException, IllegalStateException {
FTPClient ftpClient = pool.take();
if(ftpClient == null){
ftpClient = factory.makeObject();
addObject();
}else if(!factory.validateObject(ftpClient)){
// 从连接池中失效
invalidateObject(ftpClient);
// 制造新的对象,并添加到池中
ftpClient = factory.makeObject();
addObject();
}
return ftpClient;
}
/**
* 归还一个对象到连接池中
*/
@Override
public void returnObject(FTPClient ftpClient) throws Exception {
if(ftpClient != null && !pool.offer(ftpClient, 3, TimeUnit.SECONDS)){
factory.destroyObject(ftpClient);
}
}
/**
* 关闭对象池,清理内存释放资源等
*/
@Override
@PreDestroy
public void close() throws Exception {
while(pool.size() > 0){
FTPClient ftpClient = pool.take();
factory.destroyObject(ftpClient);
}
}
/**
* 从连接池中移除一个对象
*/
@Override
public void invalidateObject(FTPClient client) throws Exception {
pool.remove(client);
}
/**
* 需要一个工厂来制造池中的对象
*/
@Override
public void setFactory(PoolableObjectFactory<FTPClient> factory)
throws IllegalStateException, UnsupportedOperationException {
}
@Override
public void clear() throws Exception, UnsupportedOperationException {
}
@Override
public int getNumActive() throws UnsupportedOperationException {
return 0;
}
@Override
public int getNumIdle() throws UnsupportedOperationException {
return 0;
}
}
FTPClient 工具类:
package indi.qiaolin.test.ureport.ftp;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
/**
* FTPClient 工具类,代理ftpClient得操作。
* @author qiaolin
* @version 2018年5月9日
*
*/
@Slf4j
@Setter
@ConfigurationProperties(prefix = "ftp.utils")
@Component
public class FTPClientUtils {
// FTP 客户端连接池
@Autowired
private FTPClientPool ftpClientPool;
/**
* 获取全部的文件列表
* @param path 路径
* @return
*/
public List<FTPFile> getFileList(String path){
List<FTPFile> list = new ArrayList<>();
FTPClient ftpClient = borrowObject();
try {
FTPFile[] listFiles = ftpClient.listFiles(path);
for (FTPFile ftpFile : listFiles) {
byte[] bytes = ftpFile.getName().getBytes("iso-8859-1");
ftpFile.setName(new String(bytes, "GBK"));
list.add(ftpFile);
}
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}finally {
returnObject(ftpClient);
}
return list;
}
/**
* FTP上传文件
*
* @throws Exception
*/
public boolean uploadFile(String remote, InputStream local) {
FTPClient ftpClient = borrowObject();
try {
byte[] bytes = remote.getBytes("GBK");
return ftpClient.storeFile(new String(bytes, "iso-8859-1"), local);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}finally {
returnObject(ftpClient);
}
}
/**
* FTP下载文件到本地路径
* @param path ftp文件路径
* @param locaPath 本地路径
* @return ftp文件流
* @throws FileNotFoundException
*/
public boolean downloadFile(String path, String locaPath) throws FileNotFoundException {
FileOutputStream fileOutputStream = new FileOutputStream(locaPath);
return downloadFile(locaPath, fileOutputStream);
}
/**
* FTP下载文件
* @param path 文件路径
* @throws IOException
*/
public InputStream downloadFile(String path) {
FTPClient ftpClient = borrowObject();
try {
byte[] bytes = path.getBytes("GBK");
InputStream fileStream = ftpClient.retrieveFileStream(new String(bytes, "iso-8859-1"));
log.info("文件 {} 下载成功!", path);
return fileStream;
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}finally {
returnObject(ftpClient);
}
}
/**
* FTP下载文件 到 输出流
* @param path ftp文件路径
* @param ops
* @return 是否写入成功
*/
public boolean downloadFile(String path, OutputStream ops) {
FTPClient ftpClient = borrowObject();
try {
boolean result = ftpClient.retrieveFile(path, ops);
return result;
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}finally {
returnObject(ftpClient);
}
}
/**
* FTP删除文件
*/
public boolean delete(String path) {
FTPClient ftpClient = borrowObject();
try {
boolean result = ftpClient.deleteFile(path);
return result;
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}finally {
returnObject(ftpClient);
}
}
/**
* 修改 ftp 客户端文件名称
* @param from 原文件名
* @param to 新文件名
* @return 是否成功
*/
public boolean rename(String from, String to) {
FTPClient ftpClient = borrowObject();
try {
boolean result = ftpClient.rename(from, to);
return result;
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}finally {
returnObject(ftpClient);
}
}
/**
* 获取FTP客户端
* @return
*/
private FTPClient borrowObject() {
try {
return ftpClientPool.borrowObject();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
/**
* 返还FTP客户端
* @param ftpClient FTP客户端
*/
private void returnObject(FTPClient ftpClient) {
try {
ftpClientPool.returnObject(ftpClient);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
最后,Ureport存储
自定义报表存储器
package indi.qiaolin.test.ureport.provider;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import org.apache.commons.net.ftp.FTPFile;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import com.bstek.ureport.provider.report.ReportFile;
import com.bstek.ureport.provider.report.ReportProvider;
import indi.qiaolin.test.ureport.ftp.FTPClientUtils;
import lombok.Setter;
/**
* FTP文件服务器 报表存储
* @author qiaolin
* @version 2018年5月9日
*
*/
@Setter
@Component
@ConfigurationProperties(prefix = "ureport.ftp.provider")
public class FTPProvider implements ReportProvider{
private static final String NAME = "ftp-provider";
private String basePath = "ureport_file/";
private String prefix = "ftp:";
private boolean disabled;
@Autowired
private FTPClientUtils ftpUtils;
@Override
public InputStream loadReport(String file) {
return ftpUtils.downloadFile(getCorrectName(file));
}
@Override
public void deleteReport(String file) {
ftpUtils.delete(getCorrectName(file));
}
@Override
public List<ReportFile> getReportFiles() {
List<FTPFile> fileList = ftpUtils.getFileList(basePath);
List<ReportFile> reportFile = new ArrayList<>();
for (FTPFile ftpFile : fileList) {
Calendar timestamp = ftpFile.getTimestamp();
reportFile.add(new ReportFile(ftpFile.getName(), timestamp.getTime()));
}
return reportFile;
}
@Override
public void saveReport(String file, String content) {
ftpUtils.uploadFile(getCorrectName(file), new ByteArrayInputStream(content.getBytes()));
}
@Override
public String getName() {
return NAME ;
}
@Override
public boolean disabled() {
return disabled;
}
@Override
public String getPrefix() {
return prefix;
}
/**
* 获取没有前缀的文件名并加上FTP下存放Ureport文件夹前缀
* @param name
* @return
*/
private String getCorrectName(String name){
if(name.startsWith(prefix)){
name = name.substring(prefix.length(), name.length());
}
return basePath + name;
}
}
做完这些工作,启动程序!即可看到效果,此时已可以保存、修改、获取列表、打开等报表操作!