1. 医疗系统中.NET MVC文件夹上传功能实现方案
在医疗信息化系统中,文件上传功能是电子病历、影像资料管理等核心业务的基础支撑。传统单文件上传已无法满足实际需求,特别是当需要批量上传包含多层目录结构的检查报告、影像序列时。本文将详细介绍基于.NET MVC框架的文件夹上传完整解决方案。
医疗行业对文件上传有特殊要求:需保留原始目录结构(如DICOM影像序列)、支持大文件传输(如CT/MRI原始数据)、确保数据安全(加密传输)并兼容老旧系统(如仍在使用IE8的医疗设备工作站)。
1.1 系统架构设计
医疗文件上传系统采用分层架构:
- 表现层:基于WebUploader的HTML5上传界面,提供文件夹选择、上传进度展示
- 业务逻辑层:.NET MVC控制器处理分片上传、合并请求
- 数据访问层:文件分片临时存储、最终文件持久化(本地存储+阿里云OSS双备份)
- 安全层:前端SM4/AES加密、传输HTTPS加密、存储加密
mermaid复制graph TD
A[浏览器] -->|加密分片上传| B[MVC控制器]
B --> C[分片临时存储]
C --> D[分片合并]
D --> E[本地存储]
D --> F[OSS云存储]
E --> G[数据库记录]
F --> G
1.2 关键技术选型
1.2.1 前端技术栈
- WebUploader:支持HTML5与Flash双模式,兼容IE8+
- CryptoJS:实现前端文件加密(SM4/AES)
- FileSystem API:用于文件夹递归遍历(Chrome/Firefox)
- Flash:IE8文件夹访问后备方案
1.2.2 后端技术栈
- ASP.NET MVC 5:路由控制、请求处理
- Entity Framework:上传记录持久化
- Aliyun OSS SDK:云端存储集成
- Quartz.NET:定时清理临时分片
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心功能实现细节
2.1 文件夹上传前端实现
医疗场景需要保持DICOM影像的目录结构,关键实现如下:
javascript复制// 递归处理文件夹内容
function traverseDirectory(entries, relativePath = '') {
entries.forEach(entry => {
if (entry.isDirectory) {
const dirReader = entry.createReader()
dirReader.readEntries(entries => {
traverseDirectory(entries, `${relativePath}${entry.name}/`)
})
} else {
entry.file(file => {
file.relativePath = relativePath // 保留相对路径
uploader.addFiles(file)
})
}
})
}
// IE8特殊处理
if (isIE8) {
document.getElementById('ie8-folder-input').addEventListener('change', function() {
const files = this.files
for (let i = 0; i < files.length; i++) {
files[i].relativePath = this.value.split('\\').slice(0, -1).join('/') + '/'
uploader.addFiles(files[i])
}
})
}
关键点:通过HTML5 Directory API获取完整目录结构,IE8下使用Flash或通过input的webkitdirectory属性模拟(需用户手动选择整个文件夹)
2.2 分片上传与断点续传
医疗影像文件通常较大(单文件可达GB级),必须实现可靠的分片上传:
csharp复制// MVC控制器处理分片上传
[HttpPost]
public ActionResult UploadChunk(string fileMd5, int chunkIndex, int totalChunks,
string fileName, string relativePath)
{
var chunkFile = Path.Combine(Config.TempPath, fileMd5, chunkIndex.ToString());
// 保存分片
Request.Files[0].SaveAs(chunkFile);
// 检查是否所有分片已上传
var chunkDir = new DirectoryInfo(Path.Combine(Config.TempPath, fileMd5));
if (chunkDir.GetFiles().Length == totalChunks)
{
MergeChunks(fileMd5, fileName, relativePath);
}
return Json(new { success = true });
}
private void MergeChunks(string fileMd5, string fileName, string relativePath)
{
// 创建目标目录(保留原始结构)
var destPath = Path.Combine(Config.UploadRoot, relativePath.Trim('/'));
Directory.CreateDirectory(destPath);
// 合并文件
using (var destStream = new FileStream(Path.Combine(destPath, fileName), FileMode.Create))
{
foreach (var chunkFile in Directory.GetFiles
