Files
sftp-manager/docs/开发文档.md
liu 14289beb66 Initial commit
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-03 10:10:11 +08:00

2270 lines
58 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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
- 标准间距16px1rem
- 组件内边距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
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- JSch for SFTP -->
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.55</version>
</dependency>
<!-- H2 Database (嵌入式数据库) -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<!-- JPA for Data Persistence -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- Lombok (简化实体类代码) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- Spring Boot DevTools (热部署) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
```
### 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<T> {
private boolean success; // 操作是否成功
private String message; // 响应消息
private T data; // 响应数据
private String error; // 错误信息
}
```
**使用场景:**
- 统一所有API的响应格式
- 便于前端统一处理
- 支持泛型,灵活适配不同数据类型
### 2.4 数据传输对象DTO
**2.4.1 连接请求DTOConnectionRequest.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 文件操作请求DTOFileOperationRequest.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 文件列表请求DTOFileListRequest.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<Connection, Long> {
List<Connection> findByOrderByCreatedAtDesc(); // 按创建时间倒序查询
Optional<Connection> findByName(String name); // 按名称查询
}
```
#### 3.2.2 ConnectionService服务类
**核心方法设计:**
1. **connect(ConnectionRequest request) - 建立SFTP连接**
- 输入:连接参数(主机、端口、用户名、密码/密钥)
- 输出会话ID用于后续操作
- 实现逻辑:
- 创建JSch实例
- 配置连接参数StrictHostKeyChecking=no
- 建立Session和ChannelSftp
- 返回唯一会话IDUUID
- 异常处理:连接超时、认证失败
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<String, ChannelSftp> 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<LsEntry>转换为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
<div class="panel-container">
<div class="panel" id="left-panel">
<div class="panel-header">
<select class="mode-select">
<option value="local">本地文件</option>
<option value="sftp">SFTP服务器</option>
</select>
<input type="text" class="path-input" readonly>
</div>
<div class="file-list">
<!-- 文件列表项 -->
</div>
</div>
<div class="panel" id="right-panel">
<!-- 同左面板结构 -->
</div>
</div>
```
**文件列表项样式:**
```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
<div class="upload-area">
<input type="file" id="file-input" multiple style="display:none">
<div class="drop-zone">
<p>拖拽文件到此处或点击选择文件</p>
</div>
<div class="upload-progress">
<!-- 进度条 -->
</div>
</div>
```
**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<String> 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
<body>
<div class="app-container">
<!-- 顶部导航栏 -->
<nav class="navbar">
<div class="navbar-brand">SFTP文件管理器</div>
<div class="navbar-menu">
<button onclick="showConnectionDialog()">连接管理</button>
</div>
</nav>
<!-- 工具栏 -->
<div class="toolbar">
<button onclick="uploadFiles()">上传</button>
<button onclick="downloadFiles()">下载</button>
<button onclick="transferFiles()">传输到右侧</button>
<button onclick="deleteFiles()">删除</button>
<button onclick="showRenameDialog()">重命名</button>
<button onclick="showMkdirDialog()">新建文件夹</button>
</div>
<!-- 双面板区域 -->
<div class="panels-container">
<!-- 左面板 -->
<div class="panel" id="left-panel">
<div class="panel-header">
<select class="panel-mode" onchange="onModeChange('left')">
<option value="local">本地文件</option>
<option value="sftp">SFTP服务器</option>
</select>
<select class="connection-select" onchange="onConnectionChange('left')">
<!-- SFTP连接列表 -->
</select>
</div>
<div class="path-bar">
<button onclick="goUp('left')">↑</button>
<input type="text" class="path-input" id="left-path" readonly>
</div>
<div class="file-list" id="left-file-list">
<!-- 文件列表 -->
</div>
</div>
<!-- 右面板 -->
<div class="panel" id="right-panel">
<!-- 同左面板结构 -->
</div>
</div>
<!-- 状态栏 -->
<div class="status-bar">
<span id="status-text">就绪</span>
</div>
</div>
</body>
```
### 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
<div id="connection-dialog" class="modal">
<div class="modal-content">
<h2>连接管理</h2>
<div class="connection-list">
<!-- 已保存的连接列表 -->
</div>
<button onclick="showAddConnectionDialog()">添加连接</button>
<button onclick="closeConnectionDialog()">关闭</button>
</div>
</div>
```
**添加连接表单:**
```html
<form id="connection-form">
<input type="text" name="name" placeholder="连接名称" required>
<input type="text" name="host" placeholder="主机地址" required>
<input type="number" name="port" placeholder="端口" value="22">
<input type="text" name="username" placeholder="用户名" required>
<input type="password" name="password" placeholder="密码">
<input type="text" name="privateKeyPath" placeholder="私钥路径">
<input type="password" name="passPhrase" placeholder="私钥密码">
<button type="submit">保存</button>
<button type="button" onclick="closeAddConnectionDialog()">取消</button>
</form>
```
### 10.5 连接状态显示
**连接状态指示器:**
```html
<div class="connection-status" id="left-status">
<span class="status-dot" data-status="connected"></span>
<span class="status-text">已连接</span>
</div>
```
**状态样式:**
```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;
}
```
---
## 模块11API接口设计规范
### 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<ApiResponse> 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<ApiResponse> 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<ApiResponse> 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
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/sftp-manager.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/sftp-manager.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="FILE" />
<appender-ref ref="CONSOLE" />
</root>
<logger name="com.sftp.manager" level="DEBUG" />
</configuration>
```
### 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
<properties>
<java.version>1.8</java.version>
<spring-boot.version>2.7.18</spring-boot.version>
<jsch.version>0.1.55</jsch.version>
<h2.version>2.1.214</h2.version>
<lombok.version>1.18.30</lombok.version>
</properties>
```
### 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<LsEntry> 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界面
- ✅ 基础文件操作功能