SpringBoot相关依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.10.RELEASE</version>
<relativePath/>
</parent>
<profiles>
<profile>
<id>dev</id>
<properties>
<profileActive>dev</profileActive>
<serverPort>2203</serverPort>
</properties>
<activation>
<!-- 默认启用的是dev环境配置-->
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<profile>
<id>test</id>
<properties>
<profileActive>test</profileActive>
<serverPort>2203</serverPort>
</properties>
</profile>
<profile>
<id>prod</id>
<properties>
<profileActive>prod</profileActive>
<serverPort>2203</serverPort>
</properties>
<!-- <activation>
<activeByDefault>true</activeByDefault>
</activation> -->
</profile>
</profiles>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<hibernate.version>5.2.3.Final</hibernate.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.49</version>
</dependency>
<!--使用 @ConfigurationProperties注解时添加此依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
<scope>provided</scope>
</dependency>
<!--会使用到FileUtils把文件转成二进制流-->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.0.1</version>
</dependency>
</dependencies>
@SpringBootApplication
public class MySpringBootApplication {
public static void main(String[] args) {
/**
* 不加上下面这个if判断的话,在IDE中运行没有问题;但是如果打成jar包,通过bat文件启动,会报一个null异常
*/
if (AuthConfigFactory.getFactory() == null) {
AuthConfigFactory.setFactory(new AuthConfigFactoryImpl());
}
SpringApplication.run(MySpringBootApplication.class, args);
}
}
@RestController
public class MyController {
@Autowired
FileService fileService;
/**
* 多文件的上传
* @param files 上传的文件集合
* @return
*/
@PostMapping(value = "/importFile")
public String importFile(@RequestParam("files") MultipartFile[] files) {
return fileService.importFile(files);
}
/**
* 单个文件的下载
* @param filePath 要下载的文件全路径(目录 + 文件名)
* @param response
* @return
*/
@PostMapping(value = "/downloadFile")
public String downloadFile(@RequestParam String filePath,HttpServletResponse response) {
return fileService.downloadFile(filePath,response);
}
/**
* 多个文件打包zip下载
* @param filePathArray 以逗号分割的多文件的全路径集合
* @param zipFileName 下载的压缩包名(以.zip格式结尾)
* @return
*/
@PostMapping(value = "/downloadMultiFile")
public ResponseEntity downloadMultiFile(@RequestParam String filePathArray, String zipFileName) {
return fileService.downloadMultiFile(filePathArray.split(","),zipFileName);
}
}
@Service
@Slf4j
public class FileService {
@Autowired
FtpUtil ftpUtil;
public String importFile(MultipartFile[] files){
try {
// 文件名集合
StringBuilder fileNames = new StringBuilder();
// 获取ftp服务器连接
ChannelSftp channel = ftpUtil.getChannel();
if(files.length > 0){
for(MultipartFile file:files){
if(file.getSize() > 0){
// 拼接UUID文件名 获取文件原本的文件名:file.getOriginalFilename()
String fileType = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
String fileName = UUID.randomUUID().toString().replace("-","") + fileType;
ftpUtil.uploadFile(channel,ftpUtil.getDirectory(),file.getInputStream(),fileName);
log.info("已上传文件:"+ fileName);
fileNames.append(ftpUtil.getDirectory()).append("/").append(fileName).append(",");
}
}
}
ftpUtil.disConnect(channel);
fileNames.deleteCharAt(fileNames.length()-1);
// 省略存储文件路径到数据库的步骤
return "已上传完成的文件集合:"+fileNames;
} catch (IOException e) {
e.printStackTrace();
return "上传失败";
}
}
public String downloadFile(String filePath , HttpServletResponse response) {
try {
if(StringUtils.isEmpty(filePath)){
log.info("下载的文件路径为空:"+filePath);
throw new Exception();
}
ChannelSftp channel = ftpUtil.getChannel();
ftpUtil.download(channel,filePath,response);
return "下载成功!";
}catch (Exception e) {
e.printStackTrace();
return "下载失败!";
}
}
public ResponseEntity downloadMultiFile(String[] filePathArray, String zipFileName) {
try{
if(filePathArray == null || StringUtils.isEmpty(zipFileName)){
log.info("下载的文件路径或文件名异常:"+ Arrays.toString(filePathArray));
throw new Exception();
}
ChannelSftp channel = ftpUtil.getChannel();
return ftpUtil.download(channel,filePathArray,zipFileName);
} catch (Exception e) {
e.printStackTrace();
return new ResponseEntity("文件下载异常", HttpStatus.valueOf("500"));
}
}
}
默认使用dev环境
application.properties:
spring.profiles.active=@profileActive@
application-dev.properties:
ftp.server.host = 192.168.0.54
ftp.server.port = 22
ftp.server.username = ***
ftp.server.password = ***
# 存储上传文件的文件夹路径
ftp.server.directory = /home/template
__ Springboot1.5以上版本,在使用 @ConfigurationProperties注解的时候会提示:“Spring Boot Configuration Annotation Processor not found in classpath”,解决方案是在POM文件中增加spring-boot-configuration-processor依赖__
@Component
@ConfigurationProperties(prefix = "ftp.server")
@Data
public class FtpUtil {
private static final Logger LOG = LoggerFactory.getLogger(FtpUtil.class);
private String host;
private Integer port;
private String username;
private String password;
private String filePath;
public FtpUtil(){
}
public Channel getChannel(Session session) {
Channel channel = null;
try {
channel = session.openChannel("sftp");
channel.connect();
LOG.info("get Channel success!");
} catch (JSchException e) {
LOG.info("get Channel fail!", e);
}
return channel;
}
public Channel getChannel(){
Session session = null;
Channel channel = null;
try {
JSch jsch = new JSch();
jsch.getSession(username, host, port);
session = jsch.getSession(username, host, port);
session.setPassword(password);
Properties sshConfig = new Properties();
sshConfig.put("StrictHostKeyChecking", "no");
session.setConfig(sshConfig);
session.connect();
LOG.info("Session connected!");
channel = session.openChannel("sftp");
channel.connect();
LOG.info("get Channel success!");
} catch (JSchException e) {
LOG.info("get Channel failed!", e);
}
return channel;
}
public Session getSession(String host, int port, String username,
final String password) {
Session session = null;
try {
JSch jsch = new JSch();
jsch.getSession(username, host, port);
session = jsch.getSession(username, host, port);
session.setPassword(password);
Properties sshConfig = new Properties();
sshConfig.put("StrictHostKeyChecking", "no");
session.setConfig(sshConfig);
session.connect();
LOG.info("Session connected!");
} catch (JSchException e) {
LOG.info("get Channel failed!", e);
}
return session;
}
/**
* 创建文件夹
*
* @param sftp
* @param dir
* 文件夹名称
*/
public void mkdir(ChannelSftp sftp, String dir) {
try {
sftp.mkdir(dir);
System.out.println("创建文件夹成功!");
} catch (SftpException e) {
System.out.println("创建文件夹失败!");
LOG.error("error!" ,e);
}
}
/**
* @param sftp
* @param dir
* 上传目录
* @param file
* 上传文件
* @return
*/
public String uploadFile(ChannelSftp sftp, String dir, InputStream file, String fileName) {
String result = "";
try {
sftp.cd(dir);
if (file != null) {
sftp.put(file, fileName);
result = "上传成功!";
} else {
result = "文件为空!不能上传!";
}
} catch (Exception e) {
LOG.info("上传失败!", e);
result = "上传失败!";
}
return result;
}
/**
* 下载文件
*
* @param directory
* 下载目录
* @param downloadFile
* 下载的文件
* @param saveFile
* 存在本地的路径
* @param sftp
*/
public String download(String directory, String downloadFile,
String saveFile, ChannelSftp sftp) {
String result = "";
try {
sftp.cd(directory);
sftp.get(downloadFile, saveFile);
result = "下载成功!";
} catch (Exception e) {
result = "下载失败!";
LOG.info("下载失败!", e);
;
}
return result;
}
/**
* 下载文件
* @param filePath
* @param response
* @param sftp
*/
public void download(ChannelSftp sftp, String filePath, HttpServletResponse response) {
String directory = "";
String fileName = "";
if(filePath.contains("/")){
directory = filePath.substring(0,filePath.lastIndexOf("/"));
fileName = filePath.substring(filePath.lastIndexOf("/")+1);
}
String savePath = File.separator;
// String savePath =this.getClass().getResource("/").getPath();
OutputStream out = null;
FileInputStream inputStream = null;
try {
sftp.cd(directory);
File file = new File(savePath+fileName);
sftp.get(fileName, new FileOutputStream(file)); // 之前错误操作:先缓存到本地,再获取流返回给浏览器,最后删除临时文件
LOG.warn("已进入ftp服务器文件夹:"+directory);
// sftp.get(fileName,savePath);
LOG.warn("下载生成的临时文件:"+fileName);
// File file = new File(fileName);
inputStream = new FileInputStream(file);
out = response.getOutputStream();
// int b = 0;
// byte[] buffer = new byte[512];
// while (b != -1){
// b = inputStream.read(buffer);
// out.write(buffer,0,b);
// }
byte[] b = new byte[1024];
int len = -1;
while ((len = inputStream.read(b)) != -1){
out.write(b, 0, len);
}
inputStream.close();
String fileType = fileName.substring(fileName.lastIndexOf(".")+1);
if("xls".equals(fileType)||"xlsx".equals(fileType)){
}
response.setCharacterEncoding("UTF-8");
response.setHeader("content-type", "application/octet-stream");
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
out.flush();
out.close();
file.delete();
disConnect(sftp);
} catch (Exception e) {
if (LOG.isInfoEnabled()) {
LOG.info("文件下载出现异常,[{}]", e);
}
throw new RuntimeException("文件下载出现异常,[{}]", e);
} finally {
closeStream(inputStream,out);
}
}
/**
* 断掉连接
*/
public void disConnect(ChannelSftp sftp) {
try {
sftp.disconnect();
sftp.getSession().disconnect();
} catch (Exception e) {
LOG.error("error!" ,e);
}
}
/**
* 关闭流
* @param outputStream
*/
private void closeStream(InputStream inputStream,OutputStream outputStream) {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
LOG.error("error!" ,e);
}
}
if(inputStream != null){
try {
inputStream.close();
} catch (IOException e) {
LOG.error("error!" ,e);
}
}
}
/**
* 删除文件
*
* @param directory
* @param deleteFile
* @param sftp
*/
public String delete(String directory, String deleteFile, ChannelSftp sftp) {
String result = "";
try {
sftp.cd(directory);
sftp.rm(deleteFile);
result = "删除成功!";
} catch (Exception e) {
result = "删除失败!";
LOG.info("删除失败!", e);
}
return result;
}
private void closeChannel(Channel channel) {
if (channel != null) {
if (channel.isConnected()) {
channel.disconnect();
}
}
}
private void closeSession(Session session) {
if (session != null) {
if (session.isConnected()) {
session.disconnect();
}
}
}
public void closeAll(ChannelSftp sftp, Channel channel, Session session) {
try {
closeChannel(sftp);
closeChannel(channel);
closeSession(session);
} catch (Exception e) {
LOG.info("closeAll", e);
}
}
}
1. 文件上传
测试上传 txt 、xls 、png 三种格式文件,成功后返回文件路径
查看ftp服务器:
2. 多文件下载
(单文件下载不再测试)
此处传入的压缩包名字格式以zip结尾,代码中未控制
使用中文作为压缩包名会出现乱码情况,暂未解决
点击 save to a file:
根据需求改动存入ftp服务器的文件名,本例使用UUID