2270 lines
58 KiB
Markdown
2270 lines
58 KiB
Markdown
# 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
|
||
<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 连接请求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<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
|
||
- 返回唯一会话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<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;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 模块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<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界面
|
||
- ✅ 基础文件操作功能
|