# 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界面
- ✅ 基础文件操作功能