1. 银行系统大文件分片上传架构设计
在银行系统中设计文件上传功能时,我们需要考虑的核心要素包括:安全性、可靠性、高性能和兼容性。银行系统通常需要处理大量敏感文件,如客户证件扫描件、交易凭证等,这些文件往往体积较大且需要长期保存。
1.1 系统需求分析
银行系统的文件上传功能需要满足以下关键需求:
- 大文件支持:单个文件可达20GB以上
- 文件夹结构保持:上传后保持原始目录层级
- 断点续传:网络中断后可从断点继续
- 浏览器兼容:需支持IE8等老旧浏览器
- 安全传输:传输过程需加密
- 负载均衡:支持多服务器节点分担压力
1.2 技术架构选型
基于上述需求,我们采用分层架构设计:
前端层:
- 现代浏览器:HTML5 File API + IndexedDB
- IE8等老旧浏览器:Flash后备方案
- 加密:CryptoJS实现SM4/AES加密
- 进度管理:LocalStorage + Service Worker双保险
服务层:
- 文件分片:10MB为一个标准分片
- 状态管理:SQL Server记录上传状态
- 存储策略:分布式文件系统+对象存储
- 加密存储:服务器端二次加密
基础设施层:
- 负载均衡:Nginx反向代理
- 存储节点:多服务器分布式存储
- 数据库:SQL Server集群
2. 分片上传核心实现
2.1 前端分片处理逻辑
前端分片处理的核心在于将大文件切割为多个标准分片,并管理上传过程。以下是关键实现代码:
javascript复制class BigFileUploader {
constructor() {
this.chunkSize = 10 * 1024 * 1024; // 10MB标准分片
this.maxRetry = 3; // 最大重试次数
this.concurrentUploads = 3; // 并发上传数
}
async uploadFile(file, relativePath) {
const totalChunks = Math.ceil(file.size / this.chunkSize);
const fileId = this.generateFileId(file, relativePath);
// 检查已上传分片
const uploadedChunks = await this.checkUploadedChunks(fileId);
for (let chunkIndex = 0; chunkIndex < totalChunks; chunkIndex++) {
if (uploadedChunks.includes(chunkIndex)) continue;
const chunk = file.slice(
chunkIndex * this.chunkSize,
Math.min(file.size, (chunkIndex + 1) * this.chunkSize)
);
const encryptedChunk = this.encryptChunk(chunk);
await this.uploadChunk(fileId, chunkIndex, encryptedChunk);
// 保存进度
this.saveProgress(fileId, chunkIndex);
}
// 通知服务器完成上传
await this.completeUpload(fileId, totalChunks);
}
}
2.2 后端分片处理
后端需要接收并存储分片,最终合并为完整文件。以下是C#核心处理逻辑:
csharp复制public class FileUploadHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
string action = context.Request["action"];
switch (action)
{
case "upload":
HandleChunkUpload(context);
break;
case "check":
CheckChunkStatus(context);
break;
case "complete":
CompleteFileUpload(context);
break;
}
}
private void HandleChunkUpload(HttpContext context)
{
string fileId = context.Request["fileId"];
int chunkIndex = int.Parse(context.Request["chunkIndex"]);
// 获取加密分片数据
Stream chunkStream = context.Request.InputStream;
// 解密数据
byte[] decryptedData = DecryptChunk(chunkStream, "SM4");
// 存储到临时位置
string tempPath = GetChunkTempPath(fileId, chunkIndex);
File.WriteAllBytes(tempPath, decryptedData);
// 记录数据库
RecordChunkUpload(fileId, chunkIndex, tempPath);
}
}
3. 跨服务器负载均衡方案
3.1 负载均衡架构设计
在银行系统中,我们需要设计能够跨多台服务器节点的负载均衡方案,主要考虑以下因素:
-
分片分配策略:
- 轮询分配:简单但可能不均衡
- 基于服务器负载动态分配
- 基于地理位置就近分配
-
状态同步机制:
- 数据库集中记录分片位置
- Redis缓存加速查询
- 定期同步各节点状态
-
容错处理:
- 分片冗余存储
- 自动故障转移
- 断点续传支持
3.2 具体实现方案
我们采用基于Nginx的负载均衡方案,配合自定义的分片分配策略:
Nginx配置示例:
nginx复制upstream upload_servers {
server 192.168.1.101:8080 weight=5;
server 192.168.1.102:8080 weight=3;
server 192.168.1.103:8080 weight=2;
# 长连接配置
keepalive 32;
}
server {
listen 80;
server_name upload.bank.com;
location /upload {
proxy_pass http://upload_servers;
proxy_http_version 1.1;
proxy_set_header Connection "";
# 大文件上传超时设置
proxy_read_timeout 600s;
proxy_send_timeout 600s;
}
}
分片分配策略实现:
csharp复制public class ChunkDistributionStrategy
{
private static readonly List<ServerNode> _nodes = new List<ServerNode>();
private static readonly object _lock = new object();
public static ServerNode GetOptimalNode(long chunkSize)
{
lock (_lock)
{
// 1. 排除过载节点
var availableNodes = _nodes.Where(n => n.CurrentLoad < n.MaxLoad).ToList();
if (!availableNodes.Any())
throw new Exception("No available server nodes");
// 2. 选择负载最低的节点
var optimalNode = availableNodes.OrderBy(n => n.CurrentLoad).First();
// 3. 更新节点负载
optimalNode.CurrentLoad += chunkSize;
return optimalNode;
}
}
public static void ReleaseNodeLoad(ServerNode node, long chunkSize)
{
lock (_lock)
{
node.CurrentLoad -= chunkSize;
}
}
}
4. 关键问题与解决方案
4.1 大文件夹遍历性能优化
当处理包含大量文件的文件夹时,直接遍历会导致UI冻结。解决方案:
- Web Workers后台处理:
javascript复制// 主线程
const folderWorker = new Worker('folder-worker.js');
folderWorker.postMessage({folder: folder});
folderWorker.onmessage = (e) => {
// 处理文件列表
};
// folder-worker.js
self.onmessage = (e) => {
const entries = readDirectory(e.data.folder);
self.postMessage(entries);
};
- 分批次处理:
javascript复制async function processFolder(folder) {
const entries = await readDirectory(folder);
const BATCH_SIZE = 50;
for (let i = 0; i < entries.length; i += BATCH_SIZE) {
const batch = entries.slice(i, i + BATCH_SIZE);
await processBatch(batch);
// 释放UI线程
await new Promise(resolve => setTimeout(resolve, 0));
}
}
4.2 断点续传可靠性保障
为确保断点续传的可靠性,我们采用多级进度保存机制:
-
客户端存储:
- LocalStorage:存储基本进度信息
- IndexedDB:存储更详细的状态
-
服务端同步:
- 每上传完一个分片即通知服务端
- 服务端在数据库中记录分片状态
-
恢复流程:
javascript复制async function resumeUpload(fileId) {
// 1. 检查本地存储
let progress = localStorage.getItem(`upload_${fileId}`);
if (!progress) {
// 2. 查询服务端
progress = await fetchUploadProgress(fileId);
}
// 3. 恢复上传
if (progress) {
return continueUpload(fileId, progress);
}
// 4. 全新上传
return startNewUpload(fileId);
}
5. 安全设计与实现
5.1 传输加密方案
银行系统对安全性要求极高,我们采用双重加密方案:
- 客户端加密:
- 使用CryptoJS实现SM4加密
- 每个分片单独加密
- 加密密钥由服务器动态下发
javascript复制function encryptChunk(chunk, key) {
const wordArray = CryptoJS.lib.WordArray.create(chunk);
const encrypted = CryptoJS.SM4.encrypt(wordArray, key, {
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
return encrypted.toString();
}
- 服务端加密:
- 接收后使用更安全的算法二次加密
- 加密密钥由HSM硬件安全模块管理
csharp复制public byte[] ServerSideEncrypt(byte[] data)
{
using (var aes = new AesManaged())
{
aes.Key = GetEncryptionKeyFromHSM();
aes.Mode = CipherMode.CBC;
using (var encryptor = aes.CreateEncryptor())
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
cs.Write(data, 0, data.Length);
cs.FlushFinalBlock();
return ms.ToArray();
}
}
}
}
5.2 安全存储策略
文件存储采用分级安全策略:
-
临时存储区:
- 未完成上传的分片
- 定期清理(24小时)
- 较低安全级别
-
正式存储区:
- 已完成上传的文件
- 高强度加密存储
- 访问权限严格控制
-
备份存储区:
- 异地备份
- 冷存储设计
- 定期完整性校验
6. 性能优化技巧
6.1 上传性能优化
- 并发上传控制:
javascript复制class UploadQueue {
constructor(maxConcurrent = 3) {
this.maxConcurrent = maxConcurrent;
this.current = 0;
this.queue = [];
}
add(task) {
return new Promise((resolve, reject) => {
const wrappedTask = async () => {
try {
const result = await task();
resolve(result);
} catch (error) {
reject(error);
} finally {
this.current--;
this.next();
}
};
this.queue.push(wrappedTask);
this.next();
});
}
next() {
while (this.queue.length > 0 && this.current < this.maxConcurrent) {
this.current++;
const task = this.queue.shift();
task();
}
}
}
- 分片大小优化:
- 测试不同分片大小(1MB、5MB、10MB、20MB)
- 根据网络条件动态调整
- 平衡分片数量和单个分片大小
6.2 服务器端优化
-
IIS配置优化:
- 增加上传大小限制:
xml复制<system.web> <httpRuntime maxRequestLength="2147483647" /> </system.web> <system.webServer> <security> <requestFiltering> <requestLimits maxAllowedContentLength="4294967295" /> </requestFiltering> </security> </system.webServer> - 调整连接池设置
- 启用动态内容压缩
- 增加上传大小限制:
-
数据库优化:
- 为上传记录表添加合适索引
- 使用存储过程处理高频查询
- 定期归档已完成的上传记录
7. 兼容性处理方案
7.1 老旧浏览器支持
对于IE8等老旧浏览器,我们采用Flash后备方案:
- 检测与降级逻辑:
javascript复制function getUploader() {
if (window.File && window.FileReader && window.FileList && window.Blob) {
// 使用HTML5上传器
return new Html5Uploader();
} else if (hasFlash()) {
// 使用Flash上传器
return new FlashUploader();
} else {
// 完全不支持
throw new Error('Browser not supported');
}
}
- Flash上传器实现要点:
- 使用SWFObject动态加载Flash
- JavaScript与Flash通信
- 保持与HTML5版本相同的API接口
7.2 移动端适配
移动端上传需要特殊考虑:
-
网络状况处理:
- 检测网络类型(WiFi/4G)
- 在移动网络下降低并发数
- 提供暂停上传选项
-
内存管理:
- 避免一次性加载大文件
- 使用更小的分片大小(如2MB)
- 及时释放内存
8. 监控与日志系统
8.1 上传监控指标
完善的监控系统应包括:
-
实时指标:
- 上传速度
- 剩余时间
- 成功率
- 并发数
-
历史统计:
- 日/周/月上传量
- 平均上传速度
- 失败率趋势
8.2 日志记录策略
-
客户端日志:
- 关键操作记录
- 错误详细信息
- 性能数据
-
服务端日志:
- 访问日志
- 分片接收记录
- 系统异常
-
日志分析:
- 识别常见问题
- 优化热点路径
- 容量规划
9. 测试方案设计
9.1 测试用例设计
全面的测试方案应包括:
-
功能测试:
- 单文件上传
- 文件夹上传
- 断点续传
- 加密验证
-
性能测试:
- 不同文件大小
- 不同网络条件
- 高并发场景
-
兼容性测试:
- 浏览器矩阵
- 移动设备
- 操作系统
9.2 自动化测试实现
使用自动化测试框架提高效率:
- 前端测试:
javascript复制describe('FileUploader', () => {
it('should split file into correct chunks', () => {
const file = new File([new ArrayBuffer(25 * 1024 * 1024)], 'test.txt');
const uploader = new BigFileUploader();
const chunks = uploader.splitFile(file);
expect(chunks.length).toBe(3); // 25MB / 10MB = 3 chunks
});
});
- 后端测试:
csharp复制[TestMethod]
public void TestChunkUpload()
{
var handler = new FileUploadHandler();
var context = new MockHttpContext();
// 模拟分片上传请求
context.SetRequest("action", "upload");
context.SetRequest("fileId", "test123");
context.SetRequest("chunkIndex", "0");
context.SetInputStream(new MemoryStream(TestData));
handler.ProcessRequest(context.Object);
// 验证分片是否保存
Assert.IsTrue(File.Exists(GetChunkPath("test123", 0)));
}
10. 部署与运维
10.1 服务器部署方案
建议的服务器部署架构:
-
前端层:
- CDN加速静态资源
- 多地域部署
-
应用层:
- 至少2个节点实现高可用
- 自动伸缩组
-
存储层:
- 分布式文件系统
- 对象存储备份
- 定期快照
10.2 运维最佳实践
-
容量规划:
- 根据历史数据预测存储需求
- 预留20%缓冲空间
-
监控告警:
- 磁盘空间监控
- 上传失败告警
- 性能下降检测
-
灾备方案:
- 跨机房备份
- 定期恢复演练
- 故障自动转移
在实际部署中,我们发现将临时分片存储与最终文件存储分离可以显著提高性能。临时分片使用高速本地SSD存储,而最终文件存储在分布式文件系统中。这种分层存储设计在银行系统的大文件上传场景中表现尤为出色。