一. 服务器部分
// 安装
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: 21user: ftpfilepass: ftpfilepath: /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;@Overridepublic 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();}}@Overridepublic void destroy() throws Exception {ftpClient.disconnect();}@Bean@Scope("prototype")public FTPClient ftpClient() {return ftpClient;}@Autowiredprivate ObjectProvider<FTPClient> ftpClientProvider;public FTPClient getFtpClient() {return ftpClientProvider.getObject();}
}
- controller
@RestController
@RequestMapping("/file")
public class FileController {@Autowiredprivate 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的作用域,使得每次返回都是一个新的对象。