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; import java.io.*; import java.text.SimpleDateFormat; import java.util.Date; @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) throws IOException { 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; } }