import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/ftp")
public class FtpFileTransferController {
private static final Logger LOGGER = LoggerFactory.getLogger(FtpFileTransferController.class);
@PostMapping("/login")
public ResponseEntity<FTPClient> loginToFtp(String server, int port, String user, String pass) {
FTPClient ftpClient = new FTPClient();
try {
ftpClient.connect(server, port);
ftpClient.login(user, pass);
ftpClient.enterLocalPassiveMode();
LOGGER.info("用户 {} 登录 FTP 服务器", user);
return new ResponseEntity<>(ftpClient, HttpStatus.OK);
} catch (IOException e) {
LOGGER.error("用户 {} 连接或登录 FTP 服务器时出现错误: {}", user, e.getMessage());
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@PostMapping("/download")
public ResponseEntity<String> downloadFile(FTPClient ftpClient, String remotePath, String localPath, String operator) {
try {
FTPFile[] files = ftpClient.listFiles(remotePath);
if (files.length > 0) {
FTPFile file = files[0];
String originalFileName = file.getName();
File localFile = getUniqueLocalFile(originalFileName, localPath);
OutputStream outputStream = new FileOutputStream(localFile);
try {
ftpClient.retrieveFile(remotePath, outputStream);
LOGGER.info("用户 {} 下载文件 {}", operator, remotePath);
return new ResponseEntity<>("下载成功", HttpStatus.OK);
} catch (IOException e) {
LOGGER.error("用户 {} 下载文件 {} 时出现错误: {}", operator, remotePath, e.getMessage());
return new ResponseEntity<>("下载失败", HttpStatus.INTERNAL_SERVER_ERROR);
} finally {
try {
outputStream.close();
} catch (IOException e) {
LOGGER.error("用户 {} 关闭输出流时出现错误: {}", operator, e.getMessage());
}
}
}
LOGGER.warn("用户 {} 尝试下载文件,但远程路径 {} 下无文件可下载", operator, remotePath);
return new ResponseEntity<>("远程路径下无文件可下载", HttpStatus.NOT_FOUND);
} catch (IOException e) {
LOGGER.error("用户 {} 下载文件操作时出现错误: {}", operator, e.getMessage());
return new ResponseEntity<>("下载失败", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@PostMapping("/upload")
public ResponseEntity<String> uploadFile(FTPClient ftpClient, String localPath, String remotePath, String operator) {
try {
File localFile = new File(localPath);
String originalFileName = localFile.getName();
String uniqueFileName = getUniqueRemoteFileName(originalFileName, remotePath, ftpClient);
FileInputStream inputStream = new FileInputStream(localFile);
try {
ftpClient.storeFile(remotePath + File.separator + uniqueFileName, inputStream);
LOGGER.info("用户 {} 上传文件 {}", operator, localPath);
return new ResponseEntity<>("上传成功", HttpStatus.OK);
} catch (IOException e) {
LOGGER.error("用户 {} 上传文件 {} 时出现错误: {}", operator, localPath, e.getMessage());
return new ResponseEntity<>("上传失败", HttpStatus.INTERNAL_SERVER_ERROR);
} finally {
try {
inputStream.close();
} catch (IOException e) {
LOGGER.error("用户 {} 关闭输入流时出现错误: {}", operator, e.getMessage());
}
}
} catch (IOException e) {
LOGGER.error("用户 {} 上传文件操作时出现错误: {}", operator, e.getMessage());
return new ResponseEntity<>("上传失败", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@PostMapping("/createFolder")
public ResponseEntity<String> createFolder(FTPClient ftpClient, String folderPath, String operator) {
try {
boolean created = ftpClient.makeDirectory(folderPath);
if (created) {
LOGGER.info("用户 {} 创建文件夹 {}", operator, folderPath);
return new ResponseEntity<>("文件夹创建成功", HttpStatus.OK);
} else {
LOGGER.error("用户 {} 创建文件夹 {} 失败", operator, folderPath);
return new ResponseEntity<>("创建文件夹失败", HttpStatus.INTERNAL_SERVER_ERROR);
}
} catch (IOException e) {
LOGGER.error("用户 {} 创建文件夹 {} 时出现错误: {}", operator, folderPath, e.getMessage());
return new ResponseEntity<>("创建文件夹失败", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@PostMapping("/deleteFile")
public ResponseEntity<String> deleteFile(FTPClient ftpClient, String filePath, String operator) {
try {
boolean deleted = ftpClient.deleteFile(filePath);
if (deleted) {
LOGGER.info("用户 {} 删除文件 {}", operator, filePath);
return new ResponseEntity<>("文件删除成功", HttpStatus.OK);
} else {
LOGGER.error("用户 {} 删除文件 {} 失败", operator, filePath);
return new ResponseEntity<>("文件删除失败", HttpStatus.INTERNAL_SERVER_ERROR);
}
} catch (IOException e) {
LOGGER.error("用户 {} 删除文件 {} 时出现错误: {}", operator, filePath, e.getMessage());
return new ResponseEntity<>("文件删除失败", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@PostMapping("/listDirectory")
public ResponseEntity<String> listDirectory(FTPClient ftpClient, String directoryPath) {
try {
FTPFile[] files = ftpClient.listFiles(directoryPath);
StringBuilder response = new StringBuilder();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
for (FTPFile file : files) {
String name = file.getName();
Date modifiedDate = new Date(file.getTimestamp().getTimeInMillis());
String formattedDate = dateFormat.format(modifiedDate);
response.append("名称: ").append(name).append(", 修改时间: ").append(formattedDate).append("\n");
}
return new ResponseEntity<>(response.toString(), HttpStatus.OK);
} catch (IOException e) {
LOGGER.error("获取目录详情时出现错误: {}", e.getMessage());
return new ResponseEntity<>("获取目录详情失败", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
// 检查服务器端指定路径是否存在文件
public static boolean checkFileExists(FTPClient ftpClient, String filePath) throws IOException {
try {
FTPFile[] files = ftpClient.listFiles(filePath);
return files.length > 0;
} catch (IOException e) {
LOGGER.error("检查服务器端文件存在性时出现错误: {}", e.getMessage());
throw e;
}
}
// 给文件名添加数字后缀以避免冲突(服务器端)
public static String getUniqueRemoteFileName(String originalFileName, String remotePath, FTPClient ftpClient) {
int count = 1;
String newFileName = originalFileName;
while (checkFileExists(ftpClient, remotePath + File.separator + newFileName)) {
newFileName = getFileNameWithSuffix(originalFileName, count++);
}
return newFileName;
}
// 给文件名添加数字后缀以避免冲突(本地端)
public static File getUniqueLocalFile(String originalFileName, String localPath) {
int count = 1;
String newFileName = originalFileName;
File file = new File(localPath + File.separator + newFileName);
while (file.exists()) {
newFileName = getFileNameWithSuffix(originalFileName, count++);
file = new File(localPath + File.separator + newFileName);
}
return file;
}
// 获取带有数字后缀的文件名
public static String getFileNameWithSuffix(String originalFileName, int count) {
String fileNameWithoutExtension = originalFileName.substring(0, originalFileName.lastIndexOf('.'));
String fileExtension = originalFileName.substring(originalFileName.lastIndexOf('.'));
return fileNameWithoutExtension + "_" + count + fileExtension;
}
@PostMapping("/renameFolder")
public ResponseEntity<String> renameFolder(FTPClient ftpClient, String oldFolderPath, String newFolderPath) {
try {
boolean renamed = ftpClient.rename(oldFolderPath, newFolderPath);
if (renamed) {
LOGGER.info("文件夹重命名成功,旧路径: {},新路径: {}", oldFolderPath, newFolderPath);
return new ResponseEntity<>("文件夹重命名成功", HttpStatus.OK);
} else {
LOGGER.error("文件夹重命名失败,旧路径: {},新路径: {}", oldFolderPath, newFolderPath);
return new ResponseEntity<>("文件夹重命名失败", HttpStatus.INTERNAL_SERVER_ERROR);
}
} catch (IOException e) {
LOGGER.error("文件夹重命名时出现错误: {}", e.getMessage());
return new ResponseEntity<>("文件夹重命名失败", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}