一、介绍
街道一个需求,需要提供一个接口,将资质文件(图片)上传到FTP上,
因为之前是前端页面通过Node直接上传到FTP的,现在需要后台提供一个接口由后端接口上传到FTP。
二、pom依赖及配置文件
项目是否是SpringBoot工程没有关系
<!-- ftp上传下载-->
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.7</version>
</dependency>
我们将FTP的IP地址、端口号、账号、密码配置在application.yml
配置文件中,便于统一管理。
三、Controller控制层接收文件
控制层的接口入参使用MultipartFile数组
来接收文件(使用数组是为了接收多个文件):
此处需要注意,前端传的文件的name也需要为file
@PostMapping("/upload")
public ResponseData uploadFTP(@RequestParam(name = "file") MultipartFile[] imgs){
log.info("文件长度:"+imgs.length);
ResponseData<Object> data = ResponseData.defaultSuccess();
try {
for (MultipartFile img : imgs) {
log.info("文件名:"+img.getOriginalFilename());
}
String[] url = uploadFTPService.uploadFTP(imgs);
data.setData(url);
}catch (Exception e){
data = ResponseData.defaultFail();
data.setMessage(e.getMessage());
}
return data;
}
四、Service业务层
业务层首先
-
连接FTP
-
登陆FTP
-
创建FTP目录
-
切换工作目录
-
遍历数组,将文件以
InputStream
流的形式上到FTP,最后返回上传的FTP地址。
/**
* @author :LiuShihao
* @date :Created in 2021/7/19 4:00 下午
* @desc :
*/
@Slf4j
@Service
public class UploadFTPServiceImpl implements UploadFTPService {
@Value("${ftp.drFtpPath}")
public String FTP_IP ;
@Value("${ftp.drFtpPort}")
public Integer FTP_PORT ;
@Value("${ftp.drFtpAccount}")
public String FTP_USERNAME ;
@Value("${ftp.drFtpPassword}")
public String FTP_PASSWORD ;
public String prefixPath = "/common/crm/crmfile/";
public static FTPClient ftp ;
@Override
public String[] uploadFTP(MultipartFile[] imgs) throws IOException {
String[] imagesUrls = new String[imgs.length];
ftp = new FTPClient();
ftp.connect(FTP_IP, FTP_PORT);
log.info("FTP_ip: "+FTP_IP+",port: "+FTP_PORT+",username: "+FTP_USERNAME+",password: "+FTP_PASSWORD);
boolean login = ftp.login(FTP_USERNAME, FTP_PASSWORD);
if (!login){
throw new RuntimeException("FTP连接失败!");
}else {
log.info("FTP连接成功!");
}
//进入本地被动模式
ftp.enterLocalPassiveMode();
//将 设置文件传输模式为二进制,可以保证传输的内容不会被改变 调整到登录之后
ftp.setFileType(FTP.BINARY_FILE_TYPE);
LocalDateTime now = LocalDateTime.now();
String yyyy = DateTimeFormatter.ofPattern("yyyy").format(now);
String MM = DateTimeFormatter.ofPattern("MM").format(now);
String dd = DateTimeFormatter.ofPattern("dd").format(now);
String HH = DateTimeFormatter.ofPattern("HH").format(now);
String path = prefixPath +yyyy+"/"+MM+"/"+dd+"/"+HH+"/";
log.info("FTP上传的路径:"+path);
//创建目录
boolean makeDirectory = ftp.makeDirectory(path);
//切换FTP工作目录
boolean b = ftp.changeWorkingDirectory(path);
if (!b){
throw new RuntimeException("FTP切换工作目录失败!");
}else {
log.info("FTP切换工作目录成功!");
}
int i = 0 ;
for (MultipartFile img : imgs) {
InputStream inputStream = img.getInputStream();
//storeFile 上传FTP
boolean b1 = ftp.storeFile(img.getOriginalFilename(), inputStream);
if (!b1){
throw new RuntimeException("FTP上传失败!");
}else {
log.info(img.getOriginalFilename()+"上传成功");
}
imagesUrls[i] = "http://116.228.55.176"+path+img.getOriginalFilename();
i ++;
}
return imagesUrls;
}
}
五、上传测试
Postman上传测试:
服务器日志打印:
六、如果参数是Base64编码格式的文件
如果参数不是MultipartFile类型的而是经过Base64编码格式的字符串
ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64.getDecoder().decode(fileBase64.getBytes(Charsets.UTF_8)));
只需要将Base64编码
的字符串通过Base64解码,通过ByteArrayInputStream
构造一个输入流inputStream
, 之后还是和之前的一样使用ftp.storeFile(img.getFileName(), inputStream);
将输入流上传即可。