Spring boot 集成 ureport (四) 报表存储至FTP文件服务器

声明:代码为核心代码,为了简洁!完整代码请移步我的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; 
	}
	
}
做完这些工作,启动程序!即可看到效果,此时已可以保存、修改、获取列表、打开等报表操作!

猜你喜欢

转载自blog.csdn.net/qq_35170213/article/details/80290797