# SFTP文件管理系统 - 开发文档 --- ## 🎨 UI设计系统概览 > **完整设计系统文档请参考:** `UI设计系统.md` ### 核心设计原则 - **现代简约**:界面清晰,层次分明 - **专业高效**:减少操作步骤,提升工作效率 - **一致性**:统一的视觉语言和交互模式 - **可访问性**:符合WCAG 2.1 AA标准 ### 关键设计令牌 **颜色系统:** - 主色:`#0d6efd`(操作按钮、选中状态) - 成功:`#198754`(连接成功状态) - 危险:`#dc3545`(删除操作、错误提示) - 深灰:`#212529`(导航栏背景) - 浅灰:`#e9ecef`(工具栏背景) **字体系统:** - 字体族:系统字体栈(-apple-system, Segoe UI, Roboto等) - 正文:14px,行高1.5 - 标题:20-32px,行高1.2-1.4 - 小号文字:12px(文件大小、日期等) **间距系统:** - 基础单位:8px - 标准间距:16px(1rem) - 组件内边距:8px-16px **组件规范:** - 导航栏:高度48px,深色背景 - 工具栏:浅灰背景,按钮间距8px - 文件项:最小高度44px,悬停效果150ms - 按钮:圆角4px,过渡150ms **交互规范:** - 悬停效果:150ms过渡 - 触摸目标:最小44x44px - 键盘导航:Tab、Enter、Delete、F2、F5、Esc - 焦点状态:2px蓝色轮廓 **响应式断点:** - 移动端:< 768px(双面板垂直排列) - 平板:768px - 1024px - 桌面:> 1024px(标准布局) --- ## 项目概述 ### 1.1 项目目标 开发一个基于Spring Boot的Web版SFTP文件管理系统,支持左右双分屏界面,实现本地文件和远程SFTP服务器的文件管理功能。 ### 1.2 技术选型 - **后端框架**:Spring Boot 2.7.x - **SFTP客户端**:JSch 0.1.55 - **前端技术**:HTML5 + Bootstrap 5 + jQuery - **数据库**:H2嵌入式数据库(存储连接配置) - **JDK版本**:JDK 8+ - **构建工具**:Maven ### 1.3 核心功能特性 - ✅ 支持多SFTP服务器连接管理 - ✅ 左右双面板文件浏览 - ✅ 本地文件系统与SFTP服务器文件切换 - ✅ 支持双SFTP模式(左右均为SFTP连接) - ✅ 文件上传下载 - ✅ 文件/文件夹删除 - ✅ 文件重命名 - ✅ 新建文件夹 - ✅ 连接配置持久化 ### 1.4 架构设计 ``` ┌─────────────────────────────────────────────────────────┐ │ 浏览器前端 │ │ ┌──────────────┐ ┌──────────────┐ │ │ │ 左侧面板 │ │ 右侧面板 │ │ │ │ (本地/SFTP) │ │ (本地/SFTP) │ │ │ └──────────────┘ └──────────────┘ │ └────────────────────┬────────────────────────────────────┘ │ REST API ┌────────────────────┴────────────────────────────────────┐ │ Spring Boot 后端 │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ Controller │ │ Service │ │ Model │ │ │ │ (API接口) │ │ (业务逻辑) │ │ (数据模型) │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ └────────────────────┬────────────────────────────────────┘ │ ┌───────────┴───────────┐ │ │ ┌────────▼────────┐ ┌────────▼────────┐ │ 本地文件系统 │ │ SFTP服务器 │ └─────────────────┘ └─────────────────┘ ``` --- ## 模块01:项目初始化与基础配置 ### 1.1 项目结构创建 ``` sftp-manager/ ├── README.md # 项目说明文档 ├── pom.xml # Maven配置文件 └── src/ ├── main/ │ ├── java/com/sftp/manager/ │ │ ├── SftpManagerApplication.java # Spring Boot主类 │ │ ├── config/ │ │ │ └── WebConfig.java # Web配置(CORS等) │ │ ├── controller/ │ │ │ ├── FileController.java # 文件操作API │ │ │ └── ConnectionController.java # 连接管理API │ │ ├── service/ │ │ │ ├── SftpService.java # SFTP操作服务 │ │ │ ├── LocalFileService.java # 本地文件操作服务 │ │ │ └── ConnectionService.java # 连接配置服务 │ │ ├── model/ │ │ │ ├── Connection.java # 连接实体类 │ │ │ └── FileInfo.java # 文件信息实体类 │ │ └── dto/ │ │ ├── ConnectionRequest.java # 连接请求DTO │ │ ├── FileOperationRequest.java # 文件操作请求DTO │ │ └── ApiResponse.java # API统一响应DTO │ └── resources/ │ ├── application.yml # 应用配置文件 │ ├── static/ │ │ ├── css/ │ │ │ └── style.css # 自定义样式 │ │ └── js/ │ │ └── app.js # 前端业务逻辑 │ └── templates/ │ └── index.html # 主页面(双面板UI) └── test/ # 测试代码 ``` ### 1.2 Maven依赖配置(pom.xml) **核心依赖列表:** ```xml org.springframework.boot spring-boot-starter-web com.jcraft jsch 0.1.55 com.h2database h2 runtime org.springframework.boot spring-boot-starter-data-jpa org.projectlombok lombok true org.springframework.boot spring-boot-devtools runtime ``` ### 1.3 应用配置文件(application.yml) **配置项说明:** ```yaml server: port: 8080 # 服务端口 servlet: context-path: /sftp-manager # 应用上下文路径 spring: application: name: sftp-manager # H2数据库配置 h2: console: enabled: true # 启用H2控制台 path: /h2-console # 控制台访问路径 datasource: url: jdbc:h2:file:./data/sftp-manager # 数据库文件路径 driver-class-name: org.h2.Driver username: sa password: # JPA配置 jpa: hibernate: ddl-auto: update # 自动更新表结构 show-sql: true # 显示SQL语句 properties: hibernate: format_sql: true # 格式化SQL输出 # 文件上传配置 servlet: multipart: enabled: true max-file-size: 100MB # 单文件最大100MB max-request-size: 500MB # 总请求最大500MB # 自定义配置 app: sftp: session-timeout: 30000 # SFTP会话超时时间(ms) connection-timeout: 10000 # 连接超时时间(ms) max-retries: 3 # 连接失败重试次数 ``` ### 1.4 主类配置(SftpManagerApplication.java) **配置要点:** 1. 使用@SpringBootApplication注解,包含自动配置 2. 启用JPA仓库(@EnableJpaRepositories) 3. 配置静态资源访问路径 ### 1.5 Web配置(WebConfig.java) **配置内容:** - CORS跨域配置(允许前端访问) - 静态资源映射 - 文件上传配置 - 响应编码设置(UTF-8) --- ## 模块02:数据模型设计 ### 2.1 连接实体(Connection.java) **字段说明:** ```java @Entity @Table(name = "connections") public class Connection { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; // 连接ID(主键) private String name; // 连接名称(用户自定义) private String host; // SFTP服务器地址 private Integer port; // SFTP端口(默认22) private String username; // 用户名 @Column(columnDefinition = "TEXT") private String password; // 密码(加密存储) private String privateKeyPath; // 私钥路径(可选) private String passPhrase; // 私钥密码(可选) private Integer connectTimeout; // 连接超时时间 private String rootPath; // 默认登录后路径 private LocalDateTime createdAt; // 创建时间 private LocalDateTime updatedAt; // 更新时间 } ``` **设计要点:** - 密码字段建议加密存储(使用AES或Base64) - 支持密码认证和密钥认证两种方式 - 记录创建和更新时间便于追踪 ### 2.2 文件信息实体(FileInfo.java) **字段说明:** ```java public class FileInfo { private String name; // 文件名 private String path; // 完整路径 private long size; // 文件大小(字节) private boolean isDirectory; // 是否为目录 private LocalDateTime modifiedTime; // 修改时间 private String permissions; // 文件权限(如:-rw-r--r--) } ``` **设计要点:** - 使用DTO模式,不持久化到数据库 - 统一本地文件和SFTP文件的表示方式 - 包含文件元数据信息 ### 2.3 通用响应对象(ApiResponse.java) **设计说明:** ```java public class ApiResponse { private boolean success; // 操作是否成功 private String message; // 响应消息 private T data; // 响应数据 private String error; // 错误信息 } ``` **使用场景:** - 统一所有API的响应格式 - 便于前端统一处理 - 支持泛型,灵活适配不同数据类型 ### 2.4 数据传输对象(DTO) **2.4.1 连接请求DTO(ConnectionRequest.java)** ```java public class ConnectionRequest { private Long id; // 连接ID(用于保存配置) private String name; // 连接名称 private String host; // 主机地址 private Integer port; // 端口 private String username; // 用户名 private String password; // 密码 private String privateKeyPath; // 私钥路径 private String passPhrase; // 私钥密码 } ``` **2.4.2 文件操作请求DTO(FileOperationRequest.java)** ```java public class FileOperationRequest { private String sessionId; // 会话ID(标识是本地还是哪个SFTP连接) private String path; // 操作的文件路径 private String newName; // 新文件名(重命名使用) private String targetPath; // 目标路径(移动/复制使用) private String targetSessionId; // 目标会话ID(跨服务器传输使用) } ``` **2.4.3 文件列表请求DTO(FileListRequest.java)** ```java public class FileListRequest { private String sessionId; // 会话ID("local"表示本地,否则为SFTP连接ID) private String path; // 要浏览的目录路径 } ``` --- ## 模块03:连接管理功能 ### 3.1 功能概述 实现SFTP连接的建立、断开、保存、加载和删除功能,支持多连接同时管理。 ### 3.2 后端设计 #### 3.2.1 ConnectionRepository接口 ```java public interface ConnectionRepository extends JpaRepository { List findByOrderByCreatedAtDesc(); // 按创建时间倒序查询 Optional findByName(String name); // 按名称查询 } ``` #### 3.2.2 ConnectionService服务类 **核心方法设计:** 1. **connect(ConnectionRequest request) - 建立SFTP连接** - 输入:连接参数(主机、端口、用户名、密码/密钥) - 输出:会话ID(用于后续操作) - 实现逻辑: - 创建JSch实例 - 配置连接参数(StrictHostKeyChecking=no) - 建立Session和ChannelSftp - 返回唯一会话ID(UUID) - 异常处理:连接超时、认证失败 2. **disconnect(String sessionId) - 断开连接** - 输入:会话ID - 输出:操作结果 - 实现逻辑: - 从缓存中获取ChannelSftp - 关闭Channel和Session - 从缓存中移除会话 3. **saveConnection(Connection connection) - 保存连接配置** - 输入:连接实体 - 输出:保存后的连接实体 - 实现逻辑: - 密码加密(可选) - 设置创建/更新时间 - 保存到数据库 4. **listConnections() - 获取所有保存的连接** - 输出:连接列表(按创建时间倒序) 5. **deleteConnection(Long id) - 删除连接配置** - 输入:连接ID - 输出:操作结果 - 实现逻辑: - 先断开该连接(如果已连接) - 从数据库删除记录 #### 3.2.3 会话管理机制 **使用ConcurrentHashMap存储活跃会话:** ```java @Component public class SessionManager { private final Map activeSessions = new ConcurrentHashMap<>(); public void addSession(String sessionId, ChannelSftp channel) { activeSessions.put(sessionId, channel); } public ChannelSftp getSession(String sessionId) { return activeSessions.get(sessionId); } public void removeSession(String sessionId) { ChannelSftp channel = activeSessions.get(sessionId); if (channel != null) { channel.disconnect(); } activeSessions.remove(sessionId); } public boolean isActive(String sessionId) { return activeSessions.containsKey(sessionId); } } ``` **会话ID规则:** - "local":表示本地文件系统 - "sftp-{uuid}":表示SFTP连接会话 ### 3.3 API接口设计 #### 3.3.1 ConnectionController **接口列表:** | 方法 | 路径 | 说明 | 请求参数 | |------|------|------|----------| | POST | /api/connection/connect | 建立SFTP连接 | ConnectionRequest | | POST | /api/connection/disconnect | 断开连接 | sessionId | | POST | /api/connection/save | 保存连接配置 | Connection | | GET | /api/connection/list | 获取所有保存的连接 | - | | DELETE | /api/connection/{id} | 删除连接配置 | 路径参数id | | GET | /api/connection/active | 获取所有活跃连接 | - | **请求/响应示例:** 1. **建立连接** ``` POST /api/connection/connect Request: {"host":"192.168.1.100","port":22,"username":"root","password":"123456"} Response: {"success":true,"data":{"sessionId":"sftp-12345678-1234-1234-1234-123456789abc"}} ``` 2. **获取连接列表** ``` GET /api/connection/list Response: {"success":true,"data":[{"id":1,"name":"测试服务器","host":"192.168.1.100","port":22,...}]} ``` ### 3.4 关键技术点 1. **JSch连接配置** - 设置StrictHostKeyChecking=no(跳过主机密钥验证) - 配置连接超时和会话超时 - 支持密码和私钥两种认证方式 2. **密码加密** - 使用AES加密存储密码 - 使用Base64编码私钥内容 3. **连接健康检查** - 定时检查活跃连接状态 - 自动断开超时未使用的连接 --- ## 模块04:文件浏览功能 ### 4.1 功能概述 实现本地文件系统和SFTP服务器文件系统的浏览功能,支持目录导航、文件列表展示。 ### 4.2 后端设计 #### 4.2.1 LocalFileService本地文件服务 **核心方法:** 1. **listFiles(String path) - 列出本地目录文件** - 输入:目录路径 - 输出:FileInfo列表 - 实现逻辑: - 使用Java NIO的Files类遍历目录 - 获取文件元数据(大小、修改时间) - 过滤隐藏文件(可选) 2. **fileExists(String path) - 检查文件是否存在** - 输入:文件路径 - 输出:boolean 3. **getFileInfo(String path) - 获取文件信息** - 输入:文件路径 - 输出:FileInfo #### 4.2.2 SftpService SFTP文件服务 **核心方法:** 1. **listFiles(String sessionId, String path) - 列出SFTP目录文件** - 输入:会话ID、目录路径 - 输出:FileInfo列表 - 实现逻辑: - 从SessionManager获取ChannelSftp - 调用ls()方法获取目录内容 - 将Vector转换为FileInfo列表 - 异常处理:连接断开、路径不存在 2. **pwd(String sessionId) - 获取当前工作目录** - 输入:会话ID - 输出:当前路径 3. **cd(String sessionId, String path) - 切换目录** - 输入:会话ID、目标路径 - 输出:操作结果 #### 4.2.3 FileController文件控制器 **接口设计:** | 方法 | 路径 | 说明 | 请求参数 | |------|------|------|----------| | POST | /api/files/list | 列出文件 | FileListRequest | | POST | /api/files/info | 获取文件信息 | FileOperationRequest | | GET | /api/files/path | 获取可用路径(本地根目录、SFTP当前目录) | sessionId | **请求/响应示例:** 1. **列出本地文件** ``` POST /api/files/list Request: {"sessionId":"local","path":"C:/Users"} Response: { "success":true, "data":[ {"name":"Desktop","path":"C:/Users/Desktop","size":0,"isDirectory":true,"modifiedTime":"2024-01-01T10:00:00"}, {"name":"Documents","path":"C:/Users/Documents","size":0,"isDirectory":true,"modifiedTime":"2024-01-01T10:00:00"} ] } ``` 2. **列出SFTP文件** ``` POST /api/files/list Request: {"sessionId":"sftp-uuid","path":"/home/user"} Response: { "success":true, "data":[ {"name":"test.txt","path":"/home/user/test.txt","size":1024,"isDirectory":false,"modifiedTime":"2024-01-01T10:00:00"} ] } ``` ### 4.3 前端设计 #### 4.3.1 文件列表展示 **布局结构:** ```html
``` **文件列表项样式:** ```css .file-item { padding: 10px; cursor: pointer; display: flex; align-items: center; } .file-item:hover { background-color: #f0f0f0; } .file-item.selected { background-color: #007bff; color: white; } .file-icon { margin-right: 10px; } ``` #### 4.3.2 目录导航逻辑 **JavaScript实现:** - 点击目录项:进入子目录 - 面包屑导航:返回上级目录 - 路径输入框:直接输入路径跳转 **关键函数:** ```javascript function loadFiles(sessionId, path) { $.ajax({ url: '/api/files/list', method: 'POST', data: {sessionId: sessionId, path: path}, success: function(response) { if (response.success) { renderFileList(response.data); } } }); } ``` ### 4.4 特殊处理 1. **路径分隔符处理** - Windows系统:使用反斜杠(\) - Linux/SFTP系统:使用正斜杠(/) - 统一显示格式,底层自动转换 2. **隐藏文件处理** - 本地文件:过滤以.开头的文件(Linux/Mac) - SFTP文件:由服务器返回 3. **文件图标** - 根据文件类型显示不同图标 - 支持常见文件类型识别(图片、视频、文档等) --- ## 模块05:文件上传下载功能 ### 5.1 功能概述 实现本地与SFTP服务器之间、以及两个SFTP服务器之间的文件上传和下载功能。 ### 5.2 后端设计 #### 5.2.1 SftpService扩展方法 **上传方法:** ```java void uploadFile(String sessionId, String localPath, String remotePath); ``` - 输入:会话ID、本地文件路径、远程目标路径 - 实现逻辑: - 获取ChannelSftp实例 - 使用put()方法上传文件 - 支持进度回调(可选) - 异常处理:本地文件不存在、远程权限不足 **下载方法:** ```java void downloadFile(String sessionId, String remotePath, String localPath); ``` - 输入:会话ID、远程文件路径、本地目标路径 - 实现逻辑: - 获取ChannelSftp实例 - 使用get()方法下载文件 - 异常处理:远程文件不存在、本地空间不足 **SFTP间传输:** ```java void transferBetweenSftp(String sourceSessionId, String sourcePath, String targetSessionId, String targetPath); ``` - 输入:源会话ID、源路径、目标会话ID、目标路径 - 实现逻辑: - 从源SFTP下载到临时文件 - 上传临时文件到目标SFTP - 删除临时文件 - 性能优化:使用流式传输避免大文件占用内存 #### 5.2.2 FileController扩展接口 | 方法 | 路径 | 说明 | 请求参数 | |------|------|------|----------| | POST | /api/files/upload | 上传文件 | MultipartFile + targetInfo | | GET | /api/files/download | 下载文件 | sessionId + path | | POST | /api/files/transfer | 服务器间传输 | TransferRequest | **TransferRequest结构:** ```java public class TransferRequest { private String sourceSessionId; // 源会话ID private String sourcePath; // 源文件路径 private String targetSessionId; // 目标会话ID private String targetPath; // 目标路径 } ``` **响应示例:** ``` POST /api/files/upload Request: FormData包含file和{"targetSessionId":"sftp-uuid","targetPath":"/home/user/"} Response: {"success":true,"message":"上传成功"} ``` ### 5.3 前端设计 #### 5.3.1 上传界面 **实现方式:** 1. 拖拽上传区域 2. 点击选择文件按钮 3. 显示上传进度 **HTML结构:** ```html

拖拽文件到此处或点击选择文件

``` **JavaScript实现:** ```javascript function uploadFile(file, targetSessionId, targetPath) { let formData = new FormData(); formData.append('file', file); formData.append('targetSessionId', targetSessionId); formData.append('targetPath', targetPath); $.ajax({ url: '/api/files/upload', method: 'POST', data: formData, processData: false, contentType: false, xhr: function() { let xhr = new window.XMLHttpRequest(); xhr.upload.addEventListener('progress', function(e) { if (e.lengthComputable) { let percent = (e.loaded / e.total) * 100; updateProgress(percent); } }); return xhr; }, success: function(response) { alert(response.message); loadFiles(targetSessionId, targetPath); } }); } ``` #### 5.3.2 下载界面 **实现方式:** 1. 右键菜单选择下载 2. 双击文件下载(文件) 3. 显示下载进度 **JavaScript实现:** ```javascript function downloadFile(sessionId, path) { window.location.href = '/api/files/download?sessionId=' + encodeURIComponent(sessionId) + '&path=' + encodeURIComponent(path); } ``` #### 5.3.3 跨服务器传输 **UI交互:** 1. 左面板选择源文件 2. 右面板选择目标位置 3. 点击"传输到右侧"按钮 **JavaScript实现:** ```javascript function transferFiles() { let sourceSessionId = leftPanel.sessionId; let sourcePath = leftPanel.selectedFile.path; let targetSessionId = rightPanel.sessionId; let targetPath = rightPanel.currentPath; $.ajax({ url: '/api/files/transfer', method: 'POST', data: { sourceSessionId: sourceSessionId, sourcePath: sourcePath, targetSessionId: targetSessionId, targetPath: targetPath }, success: function(response) { alert(response.message); loadFiles(targetSessionId, targetPath); } }); } ``` ### 5.4 性能优化 1. **大文件处理** - 使用分块上传(可选) - 设置合理的超时时间 - 显示实时进度 2. **断点续传** - 记录已传输字节数 - 支持从断点继续传输 - 文件完整性校验 3. **并发控制** - 限制同时上传/下载数量 - 队列管理机制 - 任务取消功能 4. **临时文件清理** - SFTP间传输后删除临时文件 - 定期清理超时临时文件 - 使用系统临时目录 --- ## 模块06:文件删除功能 ### 6.1 功能概述 实现删除本地文件和SFTP服务器上文件的功能,支持单个文件删除和批量删除,包含删除确认机制。 ### 6.2 后端设计 #### 6.2.1 LocalFileService删除方法 **单个文件删除:** ```java boolean deleteFile(String path); ``` - 输入:文件/目录路径 - 输出:是否成功 - 实现逻辑: - 检查文件是否存在 - 如果是目录,递归删除所有子文件 - 使用Files.delete()或Files.deleteIfExists() - 异常处理:文件不存在、权限不足、文件被占用 **批量删除:** ```java int batchDelete(List paths); ``` - 输入:文件路径列表 - 输出:成功删除数量 - 实现逻辑: - 遍历路径列表 - 逐个调用deleteFile() - 统计成功和失败数量 #### 6.2.2 SftpService删除方法 **单个文件删除:** ```java boolean deleteFile(String sessionId, String path); ``` - 输入:会话ID、文件/目录路径 - 输出:是否成功 - 实现逻辑: - 获取ChannelSftp实例 - 使用rm()删除文件,rmdir()删除空目录 - 递归删除目录(自定义实现) - 异常处理:连接断开、权限不足 **递归删除目录:** ```java void deleteDirectoryRecursive(String sessionId, String path); ``` - 实现逻辑: - 列出目录内容 - 递归删除子文件和子目录 - 最后删除空目录 #### 6.2.3 FileController删除接口 | 方法 | 路径 | 说明 | 请求参数 | |------|------|------|----------| | DELETE | /api/files/delete | 删除文件/目录 | sessionId + path | | POST | /api/files/batch-delete | 批量删除 | sessionId + paths[] | **请求示例:** ``` DELETE /api/files/delete?sessionId=sftp-uuid&path=/home/user/test.txt Response: {"success":true,"message":"删除成功"} ``` ``` POST /api/files/batch-delete Request: {"sessionId":"local","paths":["C:/test/file1.txt","C:/test/file2.txt"]} Response: {"success":true,"data":{"successCount":2,"failCount":0}} ``` ### 6.3 前端设计 #### 6.3.1 删除交互 **实现方式:** 1. 选中文件后,点击删除按钮 2. 右键菜单选择删除 3. 支持键盘Delete键删除 **确认对话框:** ```javascript function confirmDelete(sessionId, paths) { let message = paths.length === 1 ? '确定要删除 ' + paths[0] + ' 吗?' : '确定要删除选中的 ' + paths.length + ' 个文件吗?'; if (confirm(message)) { deleteFiles(sessionId, paths); } } ``` #### 6.3.2 删除实现 **JavaScript代码:** ```javascript function deleteFiles(sessionId, paths) { if (paths.length === 1) { $.ajax({ url: '/api/files/delete', method: 'DELETE', data: {sessionId: sessionId, path: paths[0]}, success: function(response) { if (response.success) { alert(response.message); refreshFileList(sessionId); } } }); } else { $.ajax({ url: '/api/files/batch-delete', method: 'POST', contentType: 'application/json', data: JSON.stringify({sessionId: sessionId, paths: paths}), success: function(response) { if (response.success) { alert('成功删除 ' + response.data.successCount + ' 个文件'); refreshFileList(sessionId); } } }); } } ``` ### 6.4 安全措施 1. **删除确认** - 必须用户确认后才能执行删除 - 显示将被删除的文件数量和名称 - 防止误操作 2. **权限检查** - 删除前检查文件是否存在 - 验证当前用户是否有删除权限 - 系统文件和受保护文件禁止删除 3. **操作日志** - 记录所有删除操作 - 包含操作时间、用户、文件路径 - 便于审计和追踪 4. **回收站机制(可选)** - 删除时移动到回收站而非直接删除 - 支持从回收站恢复文件 - 定期清理回收站 --- ## 模块07:文件重命名功能 ### 7.1 功能概述 实现重命名本地文件和SFTP服务器上文件的功能,支持单个文件重命名。 ### 7.2 后端设计 #### 7.2.1 LocalFileService重命名方法 ```java boolean renameFile(String oldPath, String newPath); ``` - 输入:旧路径、新路径 - 输出:是否成功 - 实现逻辑: - 检查旧文件是否存在 - 检查新文件名是否已存在 - 使用Files.move()重命名 - 异常处理:文件不存在、目标文件名冲突、权限不足 #### 7.2.2 SftpService重命名方法 ```java boolean renameFile(String sessionId, String oldPath, String newPath); ``` - 输入:会话ID、旧路径、新路径 - 输出:是否成功 - 实现逻辑: - 获取ChannelSftp实例 - 使用rename()方法 - 异常处理:连接断开、文件不存在、权限不足 #### 7.2.3 FileController重命名接口 | 方法 | 路径 | 说明 | 请求参数 | |------|------|------|----------| | POST | /api/files/rename | 重命名文件 | sessionId + oldPath + newPath | **请求示例:** ``` POST /api/files/rename Request: {"sessionId":"local","oldPath":"C:/test/old.txt","newPath":"C:/test/new.txt"} Response: {"success":true,"message":"重命名成功"} ``` ### 7.3 前端设计 #### 7.3.1 重命名交互 **实现方式:** 1. 选中文件后,点击重命名按钮 2. 右键菜单选择重命名 3. 双击文件名(如果未实现双击进入目录) **重命名对话框:** ```javascript function showRenameDialog(sessionId, oldPath, oldName) { let newName = prompt('请输入新文件名:', oldName); if (newName && newName !== oldName) { let newPath = getParentPath(oldPath) + '/' + newName; renameFile(sessionId, oldPath, newPath); } } ``` #### 7.3.2 重命名实现 ```javascript function renameFile(sessionId, oldPath, newPath) { $.ajax({ url: '/api/files/rename', method: 'POST', contentType: 'application/json', data: JSON.stringify({ sessionId: sessionId, oldPath: oldPath, newPath: newPath }), success: function(response) { if (response.success) { alert(response.message); refreshFileList(sessionId); } else { alert('重命名失败: ' + response.message); } } }); } ``` ### 7.4 输入验证 1. **文件名验证** - 不能包含特殊字符(如:\ / : * ? " < > |) - 不能为空 - 长度限制(如:255字符) 2. **同名检查** - 检查目标目录是否已存在同名文件 - 如存在则提示用户确认覆盖 3. **扩展名保留** - 可选:保留原文件扩展名 - 仅允许修改文件名部分 --- ## 模块08:新建文件夹功能 ### 8.1 功能概述 实现在本地文件系统和SFTP服务器上创建新文件夹的功能。 ### 8.2 后端设计 #### 8.2.1 LocalFileService创建目录方法 ```java boolean createDirectory(String path); ``` - 输入:目录路径 - 输出:是否成功 - 实现逻辑: - 检查父目录是否存在 - 检查目录名是否已存在 - 使用Files.createDirectories()创建 - 异常处理:父目录不存在、目录名冲突、权限不足 #### 8.2.2 SftpService创建目录方法 ```java boolean createDirectory(String sessionId, String path); ``` - 输入:会话ID、目录路径 - 输出:是否成功 - 实现逻辑: - 获取ChannelSftp实例 - 使用mkdir()方法 - 如需创建多级目录,递归创建 - 异常处理:连接断开、权限不足 #### 8.2.3 FileController创建目录接口 | 方法 | 路径 | 说明 | 请求参数 | |------|------|------|----------| | POST | /api/files/mkdir | 创建文件夹 | sessionId + path | **请求示例:** ``` POST /api/files/mkdir Request: {"sessionId":"sftp-uuid","path":"/home/user/newfolder"} Response: {"success":true,"message":"创建成功"} ``` ### 8.3 前端设计 #### 8.3.1 新建文件夹交互 **实现方式:** 1. 点击"新建文件夹"按钮 2. 右键菜单选择"新建文件夹" 3. 在当前路径创建 **输入对话框:** ```javascript function showMkdirDialog(sessionId, currentPath) { let folderName = prompt('请输入文件夹名称:', '新建文件夹'); if (folderName) { let newPath = currentPath + '/' + folderName; createDirectory(sessionId, newPath); } } ``` #### 8.3.2 创建实现 ```javascript function createDirectory(sessionId, path) { $.ajax({ url: '/api/files/mkdir', method: 'POST', contentType: 'application/json', data: JSON.stringify({ sessionId: sessionId, path: path }), success: function(response) { if (response.success) { alert(response.message); refreshFileList(sessionId); } else { alert('创建失败: ' + response.message); } } }); } ``` ### 8.4 输入验证 1. **目录名验证** - 不能包含特殊字符 - 不能为空 - 不能使用系统保留名称(如CON, PRN等) 2. **同名检查** - 检查目标位置是否已存在同名目录 3. **路径处理** - 自动拼接父路径 - 处理路径分隔符 --- ## 模块09:双面板UI界面设计 ### 9.1 界面布局 **整体结构:** ```html
就绪
``` ### 9.2 样式设计 **主要样式:** ```css .app-container { display: flex; flex-direction: column; height: 100vh; background-color: #f5f5f5; } .navbar { background-color: #343a40; color: white; padding: 10px 20px; display: flex; justify-content: space-between; } .toolbar { background-color: #e9ecef; padding: 10px; display: flex; gap: 10px; } .panels-container { display: flex; flex: 1; overflow: hidden; } .panel { flex: 1; display: flex; flex-direction: column; border: 1px solid #dee2e6; background-color: white; } .panel:first-child { border-right: 2px solid #6c757d; } .file-list { flex: 1; overflow-y: auto; } .file-item { padding: 8px 10px; cursor: pointer; display: flex; align-items: center; border-bottom: 1px solid #f0f0f0; } .file-item:hover { background-color: #f8f9fa; } .file-item.selected { background-color: #007bff; color: white; } ``` ### 9.3 响应式设计 **移动端适配:** ```css @media (max-width: 768px) { .panels-container { flex-direction: column; } .panel { height: 50%; } .toolbar { flex-wrap: wrap; } } ``` ### 9.4 交互设计 1. **文件选择** - 单击选中文件 - Ctrl+单击多选 - Shift+单击范围选择 - 双击进入目录或打开文件 2. **拖拽操作** - 文件拖拽到另一个面板进行传输 - 拖拽多个文件批量传输 3. **快捷键支持** - Delete:删除文件 - F2:重命名 - F5:刷新 - Backspace:返回上级目录 - Ctrl+A:全选 --- ## 模块10:模式切换功能 ### 10.1 功能概述 实现左侧和右侧面板在"本地文件"和"SFTP服务器"模式之间切换,支持同时连接多个SFTP服务器。 ### 10.2 模式状态管理 **JavaScript状态管理:** ```javascript const panelState = { left: { mode: 'local', // 'local' 或 'sftp' sessionId: 'local', // 'local' 或 SFTP会话ID currentPath: '', // 当前路径 selectedFiles: [] // 选中的文件 }, right: { mode: 'local', sessionId: 'local', currentPath: '', selectedFiles: [] } }; ``` ### 10.3 切换逻辑 **本地模式切换到SFTP模式:** ```javascript function switchToSftpMode(panelId) { // 显示SFTP连接选择器 $(`#${panelId} .connection-select`).show(); // 加载已保存的连接列表 loadConnections(); // 默认选择第一个连接(如果有) const firstConnection = getFirstConnection(); if (firstConnection) { connectToSftp(panelId, firstConnection); } } ``` **SFTP模式切换到本地模式:** ```javascript function switchToLocalMode(panelId) { // 隐藏SFTP连接选择器 $(`#${panelId} .connection-select`).hide(); // 更新会话ID为local panelState[panelId].mode = 'local'; panelState[panelId].sessionId = 'local'; panelState[panelId].currentPath = getDefaultLocalPath(); // 加载本地文件列表 loadFiles(panelId); } ``` ### 10.4 SFTP连接管理UI **连接管理对话框:** ```html ``` **添加连接表单:** ```html
``` ### 10.5 连接状态显示 **连接状态指示器:** ```html
已连接
``` **状态样式:** ```css .status-dot { width: 10px; height: 10px; border-radius: 50%; display: inline-block; margin-right: 5px; } .status-dot[data-status="connected"] { background-color: #28a745; } .status-dot[data-status="disconnected"] { background-color: #dc3545; } .status-dot[data-status="connecting"] { background-color: #ffc107; } ``` --- ## 模块11:API接口设计规范 ### 11.1 RESTful设计原则 **URL设计规范:** - 使用名词复数形式(/api/files) - 使用HTTP方法表示操作类型 - 使用查询参数传递可选参数 - 使用路径参数传递必需参数 **HTTP方法映射:** | 方法 | 操作 | 示例 | |------|------|------| | GET | 查询 | GET /api/files/list | | POST | 创建 | POST /api/files/mkdir | | PUT | 更新 | PUT /api/files/rename | | DELETE | 删除 | DELETE /api/files/delete | ### 11.2 完整API列表 #### 11.2.1 连接管理API ``` # 建立SFTP连接 POST /api/connection/connect Request Body: { "name": "测试服务器", "host": "192.168.1.100", "port": 22, "username": "root", "password": "123456" } Response: { "success": true, "message": "连接成功", "data": { "sessionId": "sftp-12345678-1234-1234-1234-123456789abc" } } # 断开连接 POST /api/connection/disconnect Request Body: { "sessionId": "sftp-12345678-1234-1234-1234-123456789abc" } Response: { "success": true, "message": "断开成功" } # 保存连接配置 POST /api/connection/save Request Body: { "name": "测试服务器", "host": "192.168.1.100", "port": 22, "username": "root", "password": "encrypted_password" } Response: { "success": true, "message": "保存成功", "data": { "id": 1, "name": "测试服务器", ... } } # 获取所有保存的连接 GET /api/connection/list Response: { "success": true, "data": [ { "id": 1, "name": "测试服务器", "host": "192.168.1.100", ... } ] } # 删除连接配置 DELETE /api/connection/{id} Response: { "success": true, "message": "删除成功" } # 获取活跃连接列表 GET /api/connection/active Response: { "success": true, "data": [ { "sessionId": "sftp-12345678-1234-1234-1234-123456789abc", "connection": { "name": "测试服务器", "host": "192.168.1.100" } } ] } ``` #### 11.2.2 文件操作API ``` # 列出文件 POST /api/files/list Request Body: { "sessionId": "sftp-12345678-1234-1234-1234-123456789abc", "path": "/home/user" } Response: { "success": true, "data": [ { "name": "test.txt", "path": "/home/user/test.txt", "size": 1024, "isDirectory": false, "modifiedTime": "2024-01-01T10:00:00", "permissions": "-rw-r--r--" } ] } # 上传文件 POST /api/files/upload Request: FormData - file: MultipartFile - targetSessionId: String - targetPath: String Response: { "success": true, "message": "上传成功" } # 下载文件 GET /api/files/download?sessionId={sessionId}&path={path} Response: 文件流 # 服务器间传输 POST /api/files/transfer Request Body: { "sourceSessionId": "sftp-12345678-1234-1234-1234-123456789abc", "sourcePath": "/home/user/test.txt", "targetSessionId": "sftp-87654321-4321-4321-4321-cba987654321", "targetPath": "/home/target/" } Response: { "success": true, "message": "传输成功" } # 删除文件 DELETE /api/files/delete?sessionId={sessionId}&path={path} Response: { "success": true, "message": "删除成功" } # 批量删除 POST /api/files/batch-delete Request Body: { "sessionId": "local", "paths": ["C:/test/file1.txt", "C:/test/file2.txt"] } Response: { "success": true, "data": { "successCount": 2, "failCount": 0 } } # 重命名 POST /api/files/rename Request Body: { "sessionId": "sftp-12345678-1234-1234-1234-123456789abc", "oldPath": "/home/user/old.txt", "newPath": "/home/user/new.txt" } Response: { "success": true, "message": "重命名成功" } # 新建文件夹 POST /api/files/mkdir Request Body: { "sessionId": "local", "path": "C:/test/newfolder" } Response: { "success": true, "message": "创建成功" } ``` ### 11.3 统一响应格式 **成功响应:** ```json { "success": true, "message": "操作成功", "data": {} } ``` **失败响应:** ```json { "success": false, "message": "操作失败", "error": "错误详细信息" } ``` ### 11.4 错误码定义 | 错误码 | 说明 | |--------|------| | 400 | 请求参数错误 | | 401 | 未授权(连接失败) | | 404 | 资源不存在 | | 500 | 服务器内部错误 | --- ## 模块12:错误处理与日志 ### 12.1 全局异常处理 **自定义异常类:** ```java public class SftpException extends RuntimeException { private String errorCode; public SftpException(String message) { super(message); } public SftpException(String errorCode, String message) { super(message); this.errorCode = errorCode; } } public class FileOperationException extends RuntimeException { private String filePath; public FileOperationException(String message, String filePath) { super(message); this.filePath = filePath; } } ``` **全局异常处理器:** ```java @RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(SftpException.class) public ResponseEntity handleSftpException(SftpException e) { ApiResponse response = new ApiResponse(); response.setSuccess(false); response.setMessage("SFTP操作失败"); response.setError(e.getMessage()); return ResponseEntity.status(500).body(response); } @ExceptionHandler(FileOperationException.class) public ResponseEntity handleFileOperationException(FileOperationException e) { ApiResponse response = new ApiResponse(); response.setSuccess(false); response.setMessage("文件操作失败"); response.setError(e.getMessage()); return ResponseEntity.status(500).body(response); } @ExceptionHandler(Exception.class) public ResponseEntity handleException(Exception e) { ApiResponse response = new ApiResponse(); response.setSuccess(false); response.setMessage("系统错误"); response.setError(e.getMessage()); return ResponseEntity.status(500).body(response); } } ``` ### 12.2 日志配置 **Logback配置(logback-spring.xml):** ```xml logs/sftp-manager.log logs/sftp-manager.%d{yyyy-MM-dd}.log 30 %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n ``` ### 12.3 操作日志记录 **操作日志实体:** ```java @Entity @Table(name = "operation_logs") public class OperationLog { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String operationType; // 操作类型:upload, download, delete等 private String sessionId; // 会话ID private String sourcePath; // 源路径 private String targetPath; // 目标路径 private boolean success; // 是否成功 private String errorMessage; // 错误信息 private LocalDateTime operationTime; // 操作时间 } ``` **操作日志服务:** ```java @Service public class OperationLogService { @Autowired private OperationLogRepository logRepository; public void logOperation(String operationType, String sessionId, String sourcePath, String targetPath, boolean success, String errorMessage) { OperationLog log = new OperationLog(); log.setOperationType(operationType); log.setSessionId(sessionId); log.setSourcePath(sourcePath); log.setTargetPath(targetPath); log.setSuccess(success); log.setErrorMessage(errorMessage); log.setOperationTime(LocalDateTime.now()); logRepository.save(log); } } ``` ### 12.4 错误信息展示 **前端错误处理:** ```javascript function handleError(xhr, status, error) { let errorMessage = '操作失败'; try { const response = JSON.parse(xhr.responseText); if (response.error) { errorMessage = response.error; } else if (response.message) { errorMessage = response.message; } } catch (e) { errorMessage = error || '未知错误'; } alert(errorMessage); updateStatus(errorMessage); } ``` --- ## 模块13:部署与测试 ### 13.1 部署说明 #### 13.1.1 本地运行 **步骤:** 1. 确保已安装JDK 8+ 2. 克隆或下载项目 3. 在项目根目录执行: ``` mvn clean install mvn spring-boot:run ``` 4. 访问:http://localhost:8080/sftp-manager #### 13.1.2 打包部署 **打包命令:** ``` mvn clean package ``` **运行JAR包:** ``` java -jar target/sftp-manager-1.0.0.jar ``` **自定义端口:** ``` java -jar target/sftp-manager-1.0.0.jar --server.port=8081 ``` #### 13.1.3 生产环境配置 **外部配置文件(application-prod.yml):** ```yaml server: port: 80 spring: datasource: url: jdbc:h2:file:/var/data/sftp-manager username: ${DB_USERNAME:sa} password: ${DB_PASSWORD:} logging: file: name: /var/log/sftp-manager/app.log level: root: INFO ``` **启动命令:** ``` java -jar target/sftp-manager-1.0.0.jar --spring.profiles.active=prod ``` ### 13.2 测试指南 #### 13.2.1 功能测试清单 **连接管理测试:** - [ ] 建立SFTP连接(密码认证) - [ ] 建立SFTP连接(密钥认证) - [ ] 断开连接 - [ ] 保存连接配置 - [ ] 加载保存的连接 - [ ] 删除连接配置 - [ ] 同时连接多个SFTP服务器 **文件浏览测试:** - [ ] 浏览本地文件系统 - [ ] 浏览SFTP服务器文件 - [ ] 进入子目录 - [ ] 返回上级目录 - [ ] 直接输入路径跳转 - [ ] 刷新文件列表 **文件操作测试:** - [ ] 上传单个文件到SFTP - [ ] 批量上传文件 - [ ] 从SFTP下载文件 - [ ] 删除本地文件 - [ ] 删除SFTP文件 - [ ] 删除目录(递归) - [ ] 批量删除 - [ ] 重命名文件 - [ ] 重命名目录 - [ ] 新建文件夹 **跨面板操作测试:** - [ ] 本地 → SFTP传输 - [ ] SFTP → 本地传输 - [ ] SFTP → SFTP传输 - [ ] 拖拽传输 **模式切换测试:** - [ ] 左面板本地/SFTP切换 - [ ] 右面板本地/SFTP切换 - [ ] 双SFTP模式 - [ ] 双本地模式 - [ ] 混合模式 #### 13.2.2 边界条件测试 **大文件测试:** - [ ] 上传大于100MB的文件 - [ ] 下载大于100MB的文件 - [ ] 断点续传测试 **特殊文件名测试:** - [ ] 中文文件名 - [ ] 空格文件名 - [ ] 特殊字符文件名 - [ ] 超长文件名 **权限测试:** - [ ] 无权限文件操作 - [ ] 只读文件上传 - [ ] 受保护目录访问 **异常情况测试:** - [ ] 网络中断 - [ ] 服务器断开 - [ ] 连接超时 - [ ] 磁盘空间不足 #### 13.2.3 性能测试 **测试指标:** - 文件上传/下载速度 - 目录列表加载时间 - 同时操作多个连接的响应时间 - 内存占用 **测试工具:** - Apache JMeter - Postman ### 13.3 常见问题排查 **问题1:无法连接SFTP服务器** - 检查网络连接 - 验证服务器地址和端口 - 确认用户名和密码正确 - 检查服务器SSH服务是否运行 **问题2:文件上传失败** - 检查目标目录权限 - 确认磁盘空间充足 - 查看服务器日志 **问题3:文件列表加载慢** - 检查网络延迟 - 减少目录文件数量 - 考虑分页加载 **问题4:连接频繁断开** - 检查防火墙设置 - 增加连接超时时间 - 检查服务器SSH配置 --- ## 附录 ### A. 依赖版本清单 ```xml 1.8 2.7.18 0.1.55 2.1.214 1.18.30 ``` ### B. 数据库表结构 **connections表:** ```sql CREATE TABLE connections ( id BIGINT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255) NOT NULL, host VARCHAR(255) NOT NULL, port INT NOT NULL, username VARCHAR(255) NOT NULL, password TEXT, private_key_path VARCHAR(500), pass_phrase VARCHAR(255), connect_timeout INT, root_path VARCHAR(500), created_at TIMESTAMP, updated_at TIMESTAMP ); ``` **operation_logs表:** ```sql CREATE TABLE operation_logs ( id BIGINT AUTO_INCREMENT PRIMARY KEY, operation_type VARCHAR(50) NOT NULL, session_id VARCHAR(100), source_path VARCHAR(1000), target_path VARCHAR(1000), success BOOLEAN, error_message TEXT, operation_time TIMESTAMP ); ``` ### C. JSch常用API参考 **连接配置:** ```java JSch jsch = new JSch(); Session session = jsch.getSession(username, host, port); session.setPassword(password); session.setConfig("StrictHostKeyChecking", "no"); session.connect(timeout); ``` **文件操作:** ```java ChannelSftp channel = (ChannelSftp) session.openChannel("sftp"); channel.connect(); // 列出文件 Vector files = channel.ls(path); // 上传 channel.put(localPath, remotePath); // 下载 channel.get(remotePath, localPath); // 删除 channel.rm(path); // 重命名 channel.rename(oldPath, newPath); // 新建目录 channel.mkdir(path); // 切换目录 channel.cd(path); ``` ### D. 前端API调用示例 **使用jQuery的完整示例:** ```javascript // 列出文件 function loadFiles(sessionId, path) { $.ajax({ url: '/api/files/list', method: 'POST', contentType: 'application/json', data: JSON.stringify({sessionId: sessionId, path: path}), success: function(response) { if (response.success) { renderFileList(response.data); } }, error: handleError }); } // 上传文件 function uploadFile(file, sessionId, path) { let formData = new FormData(); formData.append('file', file); formData.append('targetSessionId', sessionId); formData.append('targetPath', path); $.ajax({ url: '/api/files/upload', method: 'POST', data: formData, processData: false, contentType: false, success: function(response) { if (response.success) { loadFiles(sessionId, path); } }, error: handleError }); } ``` ### E. 开发工具推荐 **后端开发:** - IntelliJ IDEA - Eclipse - VS Code **前端开发:** - VS Code - Chrome DevTools **测试工具:** - Postman - Apache JMeter - BrowserStack(跨浏览器测试) ### F. 参考资源 - [Spring Boot官方文档](https://spring.io/projects/spring-boot) - [JSch官方文档](http://www.jcraft.com/jsch/) - [Bootstrap 5文档](https://getbootstrap.com/docs/5.0/) - [jQuery文档](https://api.jquery.com/) --- ## 更新日志 ### v1.0.0 (2024-02-02) - ✅ 初始版本发布 - ✅ 实现基本功能模块 - ✅ 支持本地和SFTP文件管理 - ✅ 双面板UI界面 - ✅ 基础文件操作功能