1.项目结构
2.依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<!-- FTP -->
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.3</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.4.2</version>
</dependency>
3.Ftp类
package com.test.ftp.config;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
//@Data 编译时能自动生成get set 方法
//@Component Ftp类交给spring 管理
//@Value("${}") 获取配置文件的属性
@Data
@Component
public class Ftp {
/**
* FTP地址
**/
@Value("${ftp.host}")
private String host;
/**
* FTP端口
**/
@Value("${ftp.port}")
private int port ;
/**
* FTP用户名
**/
@Value("${ftp.userName}")
private String userName;
/**
* FTP密码
**/
@Value("${ftp.passWord}")
private String passWord;
/**
* 目录
**/
@Value("${ftp.workDirectory}")
private String workDirectory;
/**
* FTP根目录
**/
@Value("${ftp.root}")
private String root;
/**
* 编码
**/
@Value("${ftp.encoding}")
private String encoding;
/**
* 最多连接数
**/
@Value("${ftp.maxTotal}")
private int maxTotal;
@Value("${ftp.minIdle}")
private int minIdle;
@Value("${ftp.maxIdle}")
private int maxIdle;
@Value("${ftp.maxWaitMillis}")
private int maxWaitMillis;
}
4.FtpPool
package com.test.ftp.config;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Component
public class FtpPool {
private static final Logger log = LoggerFactory.getLogger(FtpPool.class);
private final GenericObjectPool<FTPClient> internalPool;
public FtpPool(FtpClientFactory factory){
Ftp ftp = factory.getFtpInfo();
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
poolConfig.setMaxTotal(ftp.getMaxTotal());
poolConfig.setMinIdle(ftp.getMinIdle());
poolConfig.setMaxIdle(ftp.getMaxIdle());
poolConfig.setMaxWaitMillis(ftp.getMaxWaitMillis());
this.internalPool = new GenericObjectPool<>(factory,poolConfig);
log.info("初始化FTPClient...");
}
//从连接池中取连接
public FTPClient getFtpClient() {
try {
log.info("连接FTP.....");
return internalPool.borrowObject();
} catch (Exception e) {
log.error("连接FTP失败:"+e.getMessage(),e);
return null;
}
}
//将链接归还到连接池
public void returnFTPClient(FTPClient ftpClient) {
try {
internalPool.returnObject(ftpClient);
} catch (Exception e) {
log.error("归还到连接失败",e);
}
}
}
5.FtpClientFactory
package com.test.ftp.config;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.PooledObjectFactory;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.io.IOException;
@Component
public class FtpClientFactory implements PooledObjectFactory<FTPClient> {
private static final Logger log = LoggerFactory.getLogger(FtpClientFactory.class);
FtpClientFactory(){
log.info("初始化FtpFactory... ");
}
@Resource
private Ftp ftp;
@Override
public PooledObject<FTPClient> makeObject() {
log.info("创建连接到连接池...");
FTPClient ftpClient = new FTPClient();
return new DefaultPooledObject<>(ftpClient);
}
@Override
public void destroyObject(PooledObject<FTPClient> pooledObject) {
FTPClient ftpClient = pooledObject.getObject();
try {
ftpClient.logout();
if (ftpClient.isConnected()) {
ftpClient.disconnect();
}
log.info("断开连接...");
} catch (IOException e) {
log.error("断开连接失败!");
throw new RuntimeException("断开连接失败!", e);
}
}
@Override
public boolean validateObject(PooledObject<FTPClient> pooledObject) {
FTPClient ftpClient = pooledObject.getObject();
try {
log.info("检查连接状态...");
return ftpClient.sendNoOp();
} catch (IOException e) {
return false;
}
}
@Override
public void activateObject(PooledObject<FTPClient> pooledObject) throws Exception {
FTPClient ftpClient = pooledObject.getObject();
ftpClient.connect(ftp.getHost(),ftp.getPort());
ftpClient.login(ftp.getUserName(), ftp.getPassWord());
ftpClient.setControlEncoding(ftp.getEncoding());
ftpClient.changeWorkingDirectory(ftp.getWorkDirectory());
//设置上传文件类型为二进制,否则将无法打开文件
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
log.info("初始化连接...");
}
@Override
public void passivateObject(PooledObject<FTPClient> pooledObject){
FTPClient ftpClient = pooledObject.getObject();
try {
ftpClient.changeWorkingDirectory(ftp.getRoot());
ftpClient.logout();
if (ftpClient.isConnected()) {
ftpClient.disconnect();
}
log.info("钝化连接...");
} catch (IOException e) {
throw new RuntimeException("关闭连接失败!"+ e.getMessage());
}
}
public Ftp getFtpInfo() {
log.info("获取FTPPool属性...");
return ftp;
}
}
6.FtpUtil
package com.test.ftp.utils;
import com.test.ftp.config.Ftp;
import com.test.ftp.config.FtpPool;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.net.ftp.FTPClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.List;
import java.util.UUID;
@Component
public class FtpUtil {
private static final Logger log = LoggerFactory.getLogger(FtpUtil.class);
@Resource
private FtpPool rePool;
@Resource
private Ftp reFtp;
private static FtpPool pool;
private static Ftp ftp;
//PostConstruct 用在依赖关系注入完成之后需要执行的方法上,以执行任何初始化
@PostConstruct
public void init() {
pool = rePool;
ftp = reFtp;
log.info("初始化FTP完成...");
}
/**
* Description: 向FTP服务器上传文件
*
* @param inputStream 上传到FTP服务器上的文件流
* @param fileName 上传到FTP服务器上的文件名
* @return 成功返回文件名,否则返回null
*/
public static String upload(FileInputStream inputStream, String fileName,String path){
//获取 最后的小数点以及后面的字符串
String suffix = fileName.substring(fileName.lastIndexOf("."));
FTPClient ftpClient = pool.getFtpClient();
String newFileName = null;
if (ftpClient != null) {
//开始进行文件上传
newFileName = UUID.randomUUID() + suffix;
try {
//修改文件路径
ftpClient.makeDirectory(path);
ftpClient.changeWorkingDirectory(path);
//执行文件传输
if (!ftpClient.storeFile(newFileName, inputStream)) {//上传失败
throw new RuntimeException("上传失败");
}
log.info("上传成功!");
} catch (Exception e) {
log.error("上传失败:" + e.getMessage(), e);
return null;
} finally {//关闭资源
log.info("开始归还连接");
pool.returnFTPClient(ftpClient);
}
}
return newFileName;
}
/**
* Description: 下载文件
*
* @param fileName FTP服务器中的文件名
* @param resp 响应客户的响应体
*/
public static void downLoad(String fileName,String path, HttpServletResponse resp) {
FTPClient ftpClient = pool.getFtpClient();
if (ftpClient != null) {
resp.setContentType("multipart/form-data");
resp.addHeader("Content-Disposition", "attachment;fileName=" + fileName);
//将文件直接读取到响应体中
OutputStream out = null;
try {
out = resp.getOutputStream();
//路径为空时取配置文件的路径
String newPath = StringUtils.isBlank(path) ? ftp.getWorkDirectory():path;
ftpClient.retrieveFile( newPath+ "/" + fileName, out);
} catch (IOException e) {
log.error("下载失败:");
} finally {
if (out != null) {
try {
out.flush();
out.close();
} catch (IOException e) {
log.error("关闭FTP流失败:" + e.getMessage());
}
}
}
//归还连接
pool.returnFTPClient(ftpClient);
}
}
/**
* Description: 获取图片
*/
public static void getFtpImage(HttpServletResponse response,String filepath) {
//创建FTP连接
FTPClient ftp;
OutputStream outStream;
InputStream in = null;
try {
ftp = pool.getFtpClient();
//获取文件流
in = ftp.retrieveFileStream(filepath);
if(in != null) {
//获取最后小数点的往后的的字符
String picType = filepath.substring(filepath.lastIndexOf(".") + 1);
BufferedImage bufImg = ImageIO.read(in);
outStream = response.getOutputStream();
ImageIO.write(bufImg, picType, outStream);
log.info("获取图片成功");
}else {
throw new RuntimeException("文件不存在!");
}
} catch (IOException e) {
log.error("获取图片失败", e);
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
log.error("关闭流失败", e);
}
}
}
}
/**
* 删除多个文件
*/
public static Integer deleteFiles(String path, List<String> files) {
int count = 0;
FTPClient ftpClient;
try {
ftpClient = pool.getFtpClient();
ftpClient.changeWorkingDirectory(path);
for (String file : files) {
if (ftpClient.deleteFile(file)) {
log.info("文件 {} 删除成功", file);
count++;
} else {
log.info("文件 {} 删除失败", file);
}
}
log.info("共删除 {} 个文件", count);
} catch (IOException e) {
log.error("连接FTP服务器失败");
}
return count;
}
7.配置文件属性
#FTP IP 地址
ftp.host = 10.116.xxx.x
ftp.port = 21
ftp.userName = xxxxx
ftp.passWord = 123456
ftp.encoding = utf-8
ftp.workDirectory = /
ftp.root = /
#最大连接数
ftp.maxTotal = 100
#最小空闲
ftp.minIdle = 3
ftp.maxIdle = 5
#最大等待时间
ftp.maxWaitMillis = 30000000
logger.lever = debug
8.测试
package com.test.business.controller;
import com.test.ftp.utils.FtpUtil;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
@RestController
@RequestMapping("ftp")
public class FtpTestController {
@RequestMapping("/test")
public void FtpTest(HttpServletResponse response, MultipartFile file) throws IOException {
//FileInputStream inputStream = file.getInputStream();
//String fileName = file.getOriginalFilename();
FileInputStream inputStream = new FileInputStream(new File("C:/Users/admin/Desktop/1.png"));
//上传
String path = "image";
String fileName = FtpUtil.upload(inputStream, "1.png",path);
System.out.println(fileName);
//下载
FtpUtil.downLoad(fileName,path,response);
}
@RequestMapping("/getFtpImage")
public void getFtpImage(HttpServletResponse response, MultipartFile file) throws IOException {
//FileInputStream inputStream = file.getInputStream();
//String fileName = file.getOriginalFilename();
FileInputStream inputStream = new FileInputStream(new File("C:/Users/admin/Desktop/1.png"));
//上传
String path = "image";
String fileName = FtpUtil.upload(inputStream, "1.png",path);
System.out.println(fileName);
//查看图片
String filePath = path+"/"+fileName;
FtpUtil.getFtpImage(response,filePath);
}
@RequestMapping("/deleteFiles")
public void deleteFiles() throws FileNotFoundException {
//FileInputStream inputStream = file.getInputStream();
//String fileName = file.getOriginalFilename();
FileInputStream inputStream = new FileInputStream(new File("C:/Users/admin/Desktop/1.png"));
//上传
String path = "image";
String fileName = FtpUtil.upload(inputStream, "1.png",path);
//删除
List<String> files = new ArrayList<>();
files.add(fileName);
FtpUtil.deleteFiles(path,files);
}
//多文件上传
@RequestMapping(value = "/uploadFiles", method = RequestMethod.POST)
public void fileUploads(HttpServletRequest request) throws IOException {
List<MultipartFile> files = ((MultipartHttpServletRequest) request).getFiles("file");
String path = "image";
for (MultipartFile multipartFile : files) {
if (!multipartFile.isEmpty()) {
FtpUtil.upload((FileInputStream) multipartFile.getInputStream(), Objects.requireNonNull(multipartFile.getOriginalFilename()), path);
}
}
}
/** HTML
* <p>多文件上传</p>
* <form method="POST" enctype="multipart/form-data"
* action="/upload">
* Name: <input type="text" name="name"/>
* <p>文件1:<input type="file" name="file" /></p>
* <p>文件2:<input type="file" name="file" /></p>
* <p><input type="submit" value="多文件上传" /></p>
* </form>
*/
}
}