文件管理系统毕业设计:从IE8兼容到百万梦想的奇幻漂流

大家好,我就是那个被IE8折磨到怀疑人生的通讯专业大三狗。本来以为做个文件管理系统毕业设计撑死两周搞定,没想到这项目直接让我体验了一把"全栈工程师"的酸爽人生。

需求分析:我的毕业设计规格比阿里云还高

老师看完需求文档后的表情be like:😏 → 🤔 → 😨 → 🤯

  1. 10G文件上传:我家NAS都没这么大容量
  2. IE8兼容:这浏览器比我表弟年龄还大
  3. 信创全家桶:红莲花浏览器?这名字听着就像要渡我成佛
  4. 断点续传持久化:要求比我的婚姻观还坚定(虽然我母胎solo)
  5. 文件夹层级保留:这功能百度网盘SVIP都要加钱吧?

技术选型:从现代到复古的时空穿越

// 前端大文件上传核心逻辑 (原生JS + WebUploader)
class MegaUploader {
  constructor() {
    this.uploader = null;
    this.ieMode = this.detectIE(); // 检测IE版本
    this.initUploader();
  }

  // IE版本检测(比检测新冠病毒还复杂)
  detectIE() {
    const ua = window.navigator.userAgent;
    const msie = ua.indexOf('MSIE ');
    if (msie > 0) return parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10);
    return false;
  }

  initUploader() {
    if (this.ieMode && this.ieMode <= 9) {
      // IE专属Flash方案(仿佛回到2010年)
      this.initFlashUploader();
    } else {
      // 现代浏览器H5方案
      this.initH5Uploader();
    }
  }

  // H5上传方案(主推)
  initH5Uploader() {
    this.uploader = new WebUploader.create({
      // 基础配置
      dnd: '#filePicker',
      disableGlobalDnd: true,
      paste: document.body,
      swf: '/static/Uploader.swf', // 给IE准备的备胎
      server: '/api/upload',
      fileSizeLimit: 10 * 1024 * 1024 * 1024, // 10GB
      fileSingleSizeLimit: 5 * 1024 * 1024 * 1024, // 5GB单文件限制
      
      // 分片配置
      chunked: true,
      chunkSize: 2 * 1024 * 1024, // 2MB每片
      threads: 3, // 并发数
      chunkRetry: 2, // 失败重试
      
      // 文件夹上传配置
      duplicate: true,
      accept: {
        title: 'Files',
        extensions: '*',
        mimeTypes: '*'
      }
    });

    this.bindEvents();
  }

  // 绑定事件(比绑螃蟹还复杂)
  bindEvents() {
    // 文件添加事件
    this.uploader.on('fileQueued', file => {
      console.log(`文件 ${file.name} 已添加,大小 ${this.formatSize(file.size)}`);
      this.saveToIndexedDB(file); // 持久化存储文件信息
    });
    
    // 分片上传前加密
    this.uploader.on('uploadBeforeSend', (block, data) => {
      data.chunk = block.chunk;
      data.chunks = block.chunks;
      data.fileId = block.file.id;
      data.token = this.generateToken(block.file);
    });
    
    // 上传进度
    this.uploader.on('uploadProgress', (file, percentage) => {
      this.saveProgress(file, percentage); // 保存进度到本地
    });
  }
  
  // 加密函数(假装很安全)
  generateToken(file) {
    return btoa(`${file.name}:${file.size}:${Date.now()}`);
  }
  
  // 更多方法...
}

后端C#处理:老将新传

// UploadHandler.ashx 文件分片处理 (C# WebForm)
public class UploadHandler : IHttpHandler {
    
    public void ProcessRequest(HttpContext context) {
        context.Response.ContentType = "application/json";
        
        try {
            // 验证Token
            if (!ValidateToken(context.Request["token"])) {
                context.Response.Write(JsonConvert.SerializeObject(new {
                    error = "非法请求"
                }));
                return;
            }

            var chunk = context.Request["chunk"] != null ? int.Parse(context.Request["chunk"]) : 0;
            var chunks = context.Request["chunks"] != null ? int.Parse(context.Request["chunks"]) : 1;
            var fileId = context.Request["fileId"];
            var fileName = context.Request["name"] ?? "unknown";

            // 处理文件分片
            HttpPostedFile file = context.Request.Files[0];
            string tempPath = Path.Combine(ServerConfig.TempDir, fileId);
            
            // 确保目录存在
            Directory.CreateDirectory(tempPath);
            
            // 写入分片文件
            string chunkPath = Path.Combine(tempPath, chunk.ToString());
            using (var fs = File.OpenWrite(chunkPath)) {
                file.InputStream.CopyTo(fs);
            }

            // 如果是最后一个分片则合并
            if (chunk == chunks - 1) {
                MergeFiles(tempPath, fileName, chunks);
                EncryptAndSaveToOSS(fileName); // 加密后存OSS
            }

            context.Response.Write(JsonConvert.SerializeObject(new {
                success = true,
                chunk = chunk
            }));
        } catch (Exception ex) {
            context.Response.Write(JsonConvert.SerializeObject(new {
                error = ex.Message
            }));
        }
    }

    // 合并分片文件
    private void MergeFiles(string tempPath, string fileName, int totalChunks) {
        string destPath = Path.Combine(ServerConfig.UploadDir, fileName);
        
        using (var destStream = File.Create(destPath)) {
            for (int i = 0; i < totalChunks; i++) {
                string chunkPath = Path.Combine(tempPath, i.ToString());
                using (var sourceStream = File.OpenRead(chunkPath)) {
                    sourceStream.CopyTo(destStream);
                }
                File.Delete(chunkPath);
            }
        }
        
        Directory.Delete(tempPath);
    }

    // 更多方法...
}

数据库设计:比相亲简历还详细

-- SQL Server 数据库表设计
CREATE TABLE [dbo].[FileRecords] (
    [Id] UNIQUEIDENTIFIER PRIMARY KEY DEFAULT NEWID(),
    [FileName] NVARCHAR(255) NOT NULL,
    [FileSize] BIGINT NOT NULL,
    [FileHash] VARCHAR(64) NOT NULL,
    [StoragePath] NVARCHAR(512) NOT NULL,
    [EncryptKey] VARCHAR(512) NOT NULL, -- 加密密钥
    [UploadTime] DATETIME DEFAULT GETDATE(),
    [UserId] INT NOT NULL, -- 关联用户
    [FolderId] UNIQUEIDENTIFIER NULL -- 文件夹关联
);

CREATE TABLE [dbo].[UploadProgress] (
    [FileId] UNIQUEIDENTIFIER PRIMARY KEY,
    [UserId] INT NOT NULL,
    [TotalChunks] INT NOT NULL,
    [CompletedChunks] INT NOT NULL,
    [LastUpdate] DATETIME DEFAULT GETDATE(),
    [BrowserInfo] NVARCHAR(MAX) NULL -- 存储浏览器指纹信息
);

遇到的坑:比我的发际线还高

  1. IE8兼容地狱

    • Flash需要用户手动点击才能激活
    • 控制台输出要用window.console.log判断存在性
    • AJAX请求要特殊处理
  2. 信创浏览器适配

    • 龙芯浏览器的内核是Mozilla?还是Chromium?不,它是薛定谔的内核!
    • 红莲花浏览器居然不支持ES6的箭头函数
  3. 持久化存储方案

    • IE8只能用userData或Cookie
    • 现代浏览器用IndexedDB
    • 信创浏览器?看心情选择存储方式
  4. 文件夹上传

    • WebUploader的文件夹上传在IE下是废的
    • 要自己用递归处理FileSystem API

求职宣言:全栈苦力在线接单

通过这个项目我掌握了:

  1. 考古学(IE8适配)
  2. 密码学(虽然只是调库)
  3. 心理学(调试时的自我疏导)
  4. 金融学(算红包提成)

求工作推荐!要求:

  • 不用兼容IE6(这是我的底线)
  • 老板不要求"这个需求很简单"
  • 最好能报销生发液

P.S. 那个QQ群红包是真的!我已经靠提成实现奶茶自由了!(虽然还没收到钱)

实用代码片段:文件夹上传处理

// 文件夹上传处理 (现代浏览器)
function handleFolderUpload(folder) {
    const entries = [];
    
    // 递归读取文件夹内容
    function readDirectory(directory, path = '') {
        const reader = directory.createReader();
        reader.readEntries(entries => {
            entries.forEach(entry => {
                if (entry.isFile) {
                    entry.file(file => {
                        file.fullPath = path + file.name; // 保留路径信息
                        entries.push(file);
                    });
                } else if (entry.isDirectory) {
                    readDirectory(entry, path + entry.name + '/');
                }
            });
        });
    }
    
    readDirectory(folder);
    
    // 上传所有文件
    return Promise.all(entries.map(file => {
        return new Promise((resolve, reject) => {
            const formData = new FormData();
            formData.append('file', file);
            formData.append('path', file.fullPath);
            
            fetch('/api/upload', {
                method: 'POST',
                body: formData
            }).then(resolve).catch(reject);
        });
    }));
}

// IE下的替代方案(使用ActiveX)
function ieHandleFolderUpload() {
    // 这个代码太血腥了,就不展示了
    alert('请升级浏览器到IE10以上或使用现代浏览器');
}

总结:毕业设计改变人生

这个项目教会我:

  1. 永远不要在需求评审时说"这个简单"
  2. 信创浏览器是检验真爱的新标准
  3. 10G文件上传前记得清理C盘
  4. QQ群红包可能是新型庞氏骗局(但我还是想要)

(真诚求工作推荐,会修打印机,能装Windows 7那种)

设置框架

安装.NET Framework 4.7.2
https://dotnet.microsoft.com/en-us/download/dotnet-framework/net472
框架选择4.7.2
Alt

添加3rd引用

Alt

编译项目

Alt

NOSQL

NOSQL无需任何配置可直接访问页面进行测试
Alt

SQL

使用IIS
大文件上传测试推荐使用IIS以获取更高性能。
Alt

使用IIS Express

小文件上传测试可以使用IIS Express
Alt

创建数据库

Alt

配置数据库连接信息

Alt

检查数据库配置

Alt

访问页面进行测试

Alt
相关参考:
文件保存位置

效果预览

文件上传

文件上传

文件刷新续传

支持离线保存文件进度,在关闭浏览器,刷新浏览器后进行不丢失,仍然能够继续上传
文件续传

文件夹上传

支持上传文件夹并保留层级结构,同样支持进度信息离线保存,刷新页面,关闭页面,重启系统不丢失上传进度。
文件夹上传

下载完整示例

下载完整示例

更多推荐