跨服务器上传需要用到第三方辅助,这里使用的是ftp服务器,它可以帮助我们上传下载删除文件
假如我们的项目部署在了A服务器,需要上传的的文件到B服务器就需要使用ftp 服务器来帮助了
1. 首先,我们需要在B服务器下载ftp服务器
yum install -y vsftpd
2.查看状态
systemctl status vsftpd.service
3.启动ftp
systemctl start vsftpd.service
4.关闭ftp
systemctl start vsftpd.service
5.修改配置文件
vim /etc/vsftpd/vsftpd.conf
6.
不允许匿名登录 将YES改成NO
anonymous_enable=NO
设置登录地址,添加以下目录,登录后的默认路径
local_root=/apps/lims/lims-be/static
也可以限制某个用户的访问权限
java 代码
pom
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.8.0</version>
</dependency>
代码
登录
import com.yssoo.lims.modules.core.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
import java.io.IOException;
/**
* @author :si
* @date :2023/2/17
*/
@Slf4j
public class FtpUtil {
/**
* 登录 和liunx 账号密码一致即可,也可以单独设置
* @param server ip
* @param port 端口
* @param user 账号
* @param password 密码
* @return 是否登录成功
*/
public static FTPClient login(String server, Integer port, String user, String password) {
// 创建FTPClient对象
FTPClient ftpClient = new FTPClient();
try {
// 连接FTP服务器
ftpClient.connect(server, port);
// 登录FTP服务器
ftpClient.login(user, password);
// 获取FTP服务器的响应码
int replyCode = ftpClient.getReplyCode();
// 如果响应码不是“正常”,返回
if (!FTPReply.isPositiveCompletion(replyCode)) {
log.debug("server{}", "port{}", "user{}", "pwd{}", server, port, user, password);
throw new BusinessException("ftp登入失败");
}
// 设置被动模式(passive mode)
ftpClient.enterLocalPassiveMode();
// 设置文件传输类型为二进制文件类型
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
} catch (IOException e) {
e.printStackTrace();
}
return ftpClient;
}
public static void ftpFinally(FTPClient ftpClient) {
// 关闭FTP连接
if (ftpClient.isConnected()) {
try {
ftpClient.logout();
ftpClient.disconnect();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
上传
import org.apache.commons.net.ftp.FTPClient;
import java.io.IOException;
import java.io.InputStream;
/**
* @author :si
* @date :2023/2/17
*/
public class FtpUpload {
/**
* 上传
* @param ftpClient ftp客户端
* @param remoteFile 远程文件名
* @param localFileName 本地文件流
* @return 是否成功
*/
public static Boolean upload(
FTPClient ftpClient, String remoteFile, InputStream localFileName) {
try {
return ftpClient.storeFile(remoteFile, localFileName);
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
}
预览和下载(区别在于,预览图片文本之类的可以直接在浏览器观看,下载就是下载到本地了,如果不是文本之类的俩都可以下载)
import com.yssoo.lims.modules.core.exception.BusinessException;
import org.apache.commons.net.ftp.FTPClient;
import org.springframework.http.HttpStatus;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* @author :si
* @date :2023/2/17
*/
public class FtpDownload {
/**
* 预览
* @param ftpClient ftp客户端
* @param response 相应
* @param remoteFile 远程文件名
*/
public static void previewArea(FTPClient ftpClient, HttpServletResponse response,String remoteFile){
// 获取远程文件的输入流
try (OutputStream outputStream = response.getOutputStream();
InputStream inputStream = ftpClient.retrieveFileStream(remoteFile)) {
// 如果远程文件不存在,返回
if (inputStream == null) {
return;
}
// 将远程文件的内容写入HTTP响应中
byte[] buffer = new byte[1024];
int bytesRead = -1;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 下载
* @param ftpClient ftp 客户端
* @param response 响应
* @param remoteFile 远程文件路径
* @param remoteName 远程文件名
*/
public static void downLoad(FTPClient ftpClient, HttpServletResponse response,String remoteFile,String remoteName){
// 下载文件
InputStream inputStream;
try {
inputStream = ftpClient.retrieveFileStream("/" + remoteFile);
if (inputStream == null) {
response.setStatus(HttpStatus.NOT_FOUND.value());
throw new BusinessException("下载失败");
}
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename=" + remoteName);
byte[] buffer = new byte[1024];
int len;
while ((len = inputStream.read(buffer)) != -1) {
// 输出到浏览器响应
response.getOutputStream().write(buffer, 0, len);
}
inputStream.close();
ftpClient.logout();
response.getOutputStream().flush();
} catch (IOException e) {
e.printStackTrace();
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
}
}
}
删除
import org.apache.commons.net.ftp.FTPClient;
import java.io.IOException;
/**
* @author :si
* @date :2023/2/20
*/
public class FtpDel {
/**
* 删除
* @param ftpClient ftp 客户端
* @param remoteFilePath 远程文件路径(路径+文件名)
* @return 是否成功
*/
public static boolean delFile(FTPClient ftpClient, String remoteFilePath) {
ftpClient.enterLocalPassiveMode();
try {
return ftpClient.deleteFile(remoteFilePath);
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
}
controller 层
/** 这个作用在 删除、下载、预览的路径上, /** 后面就是文件名
* 把指定URL后的字符串全部截断当成参数 这么做是为了防止URL中包含中文或者特殊字符(/等)时,匹配不了的问题
*
* @param request
* @return
*/
private static String extractPathFromPattern(final HttpServletRequest request) {
String path =
(String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
String bestMatchPattern =
(String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
return new AntPathMatcher().extractPathWithinPattern(bestMatchPattern, path);
}
/**
* 处理上传文件请求
*
* @param file 上传的文件
* @return 返回上传结果,url
*/
@PostMapping("/ftp/upload")
public R handleFileUpload(@RequestParam("file") MultipartFile file) {
return R.ok(
storageService.upload(
ftpProperties.getServer(),
ftpProperties.getPort(),
ftpProperties.getUser(),
ftpProperties.getPassword(),
file));
}
/**
* 处理预览文件请求
*
* @param response HTTP响应对象
* @param request HTTP请求
*/
@GetMapping("/ftp/static/**")
public void previewArea(HttpServletRequest request, HttpServletResponse response) {
String remoteFile = extractPathFromPattern(request);
FTPClient ftpClient =
storageService.download(
ftpProperties.getServer(),
ftpProperties.getPort(),
ftpProperties.getUser(),
ftpProperties.getPassword());
FtpDownload.previewArea(ftpClient, response, remoteFile);
FtpUtil.ftpFinally(ftpClient);
R.ok();
}
/**
* 处理下载文件请求
*
* @param response HTTP响应对象
* @param request HTTP请求
*/
@GetMapping("/ftp/download/**")
public void handleFileDownload(HttpServletRequest request, HttpServletResponse response) {
String remoteName = extractPathFromPattern(request);
log.info("downloadFile:{}", remoteName);
FTPClient ftpClient =
storageService.download(
ftpProperties.getServer(),
ftpProperties.getPort(),
ftpProperties.getUser(),
ftpProperties.getPassword());
FtpDownload.downLoad(
ftpClient,
response,
ftpProperties.getDownLoadPath() + "/" + remoteName,
remoteName);
FtpUtil.ftpFinally(ftpClient);
R.ok();
}
/**
* 处理删除文件请求
*
* @param request HTTP请求
*/
@DeleteMapping("/ftp/del/**")
public R deleteFile(HttpServletRequest request) {
String remoteName = extractPathFromPattern(request);
log.info("delFile:{}", remoteName);
FTPClient ftpClient =
storageService.download(
ftpProperties.getServer(),
ftpProperties.getPort(),
ftpProperties.getUser(),
ftpProperties.getPassword());
boolean delFile =
FtpDel.delFile(ftpClient, ftpProperties.getDownLoadPath() + "/" + remoteName);
FtpUtil.ftpFinally(ftpClient);
return R.ok(delFile);
}
service层
@Override
public String upload(
String server, int port, String user, String password, MultipartFile file) {
FTPClient ftpClient = FtpUtil.login(server, port, user, password);
String remoteFile = generateKeyByDate(file.getOriginalFilename());
Boolean upload;
try {
upload = FtpUpload.upload(ftpClient, remoteFile,file.getInputStream());
if (!upload) {
throw new BusinessException("上传文件失败");
}
} catch (IOException e) {
e.printStackTrace();
}
FtpUtil.ftpFinally(ftpClient);
return remoteFile;
}
@Override
public FTPClient download(String server, int port, String user, String password) {
return FtpUtil.login(server, port, user, password);
}
基本都在controller完成了,因为项目结构很乱,你们可以随意。
这个在yml中定义了,直接取就可以了
@Component
@ConfigurationProperties(prefix = "ftp")
@Data
public class FtpProperties {
private String server;
private int port;
private String user;
private String password;
private String downLoadPath;
}
yml
ftp:
port: 21
server: 1.1.1.224
user: root
password: root
down-load-path: /user/loacl
即可
上述下载超过100M可能会断开链接,以下亲测可以下载500+ 最大多数不清楚,可以适当扩大读取数据
public static void downLoad(
FTPClient ftpClient,
HttpServletResponse response,
String remoteFile,
String remoteName) {
ServletOutputStream outputStream = null;
InputStream inputStream = null;
try {
ftpClient.enterLocalPassiveMode();
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
// 获取输入流
inputStream = ftpClient.retrieveFileStream(remoteFile);
// 设置响应头信息
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment; filename=" + remoteName);
// 获取响应输出流
outputStream = response.getOutputStream();
// 写入数据到响应输出流
// 每次读取1MB数据
byte[] buffer = new byte[1024 * 1024];
// 设置缓冲区大小为1MB
ftpClient.setBufferSize(1024 * 1024);
int bytesRead = -1;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭流
try {
if (null != outputStream) {
outputStream.close();
}
if (null != inputStream) {
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}