一. 服务器部分
// 安装
yum install vsftpd -y
systemctl start vsftpd
// 查看ftp是否安装成功,21号端口是ftp就是安装成功
netstat -nltp | grep 21
// 创建用户名和设置用户密码
useradd ftpfile
echo "ftpfile" | passwd ftpfile --stdin
// 开启ftp的文件访问权限和传输权限
getsebool -a|grep ftp
setsebool -P ftpd_full_access on
setsebool -P tftp_home_dir on
// 重启ftp
systemctl restart vsftpd.service
// 文件夹授权,/home/images这个目录等会作为文件的保存位置
chmod a-w /home/images && chmod 777 -R /home/images
二. java代码
- pom坐标
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.9</version>
</dependency>
- nginx
// 监听端口和请求,遇到images自动去找/home/images下的文件
server {
listen 1777;
server_name _;
root /home/demo;
location /images {
alias /home/images;
}
}
- yml
ftp:
host: 服务器ip,具体可以用ifconfig查看
port: 21
user: ftpfile
pass: ftpfile
path: /home/images/
visit: 1777 # nginx配置的端口
file: http://${ftp.host}:${ftp.visit}/images/
- 创建FTPClient Bean
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
@Configuration
@Scope("prototype")
public class FtpConfig implements InitializingBean, DisposableBean {
@Value("${ftp.host}")
private String host;
@Value("${ftp.port}")
private int port;
@Value("${ftp.user}")
private String user;
@Value("${ftp.pass}")
private String pass;
@Value("${ftp.path}")
private String path;
private FTPClient ftpClient;
@Override
public void afterPropertiesSet(){
ftpClient = new FTPClient();
try {
ftpClient.connect(host, port);
ftpClient.login(user, pass);
ftpClient.enterLocalPassiveMode();
ftpClient.changeWorkingDirectory(path);
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void destroy() throws Exception {
ftpClient.disconnect();
}
@Bean
@Scope("prototype")
public FTPClient ftpClient() {
return ftpClient;
}
@Autowired
private ObjectProvider<FTPClient> ftpClientProvider;
public FTPClient getFtpClient() {
return ftpClientProvider.getObject();
}
}
- controller
@RestController
@RequestMapping("/file")
public class FileController {
@Autowired
private FtpConfig ftpConfig;
@Value("${ftp.file}")
private String ftpFile;
@SneakyThrows
@PostMapping("/putFile")
public R<String> putFile(@RequestParam MultipartFile file) {
FTPClient ftpClient = ftpConfig.getFtpClient();
String filePath = new Random().nextInt(1000)+"-"+System.currentTimeMillis()+file.getOriginalFilename();
ftpClient.storeFile(new String(filePath.getBytes("UTF-8"),"ISO-8859-1"), file.getInputStream());
ftpClient.logout();
return R.data(ftpFile+filePath);
}
}
三. 问题
在使用ftpClient时,老是出现Broken Pipe的错误。
经过分析得出,是因为ftp连接的问题。
spring默认使用单例模式,每次拿到的bean都是同一个对象。
因为调用这个接口时,第一次调用完就关闭了,后续所有的接口都会报错,假如不关闭连接,也会因为ftp连接时间的问题自动关闭,所以我这边改变了bean的作用域,使得每次返回都是一个新的对象。