news 2026/1/14 9:31:34

html富文本编辑器大附件上传插件对比与选择

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
html富文本编辑器大附件上传插件对比与选择

大三学长毕业设计救星:原生JS大文件传输系统(附完整代码)

兄弟,作为刚摸爬滚打完毕设的通讯专业学长,太懂你现在的处境了——找工作要作品,大文件上传需求卡壳,网上开源代码全是“断头路”,出了问题连个问的人都没有。别慌!我当年做毕设时也被这玩意儿折腾得凌晨三点改代码,现在把我打磨了2个月的原生JS大文件传输系统(含Vue3前端+ASP.NET WebForm后端+SQL Server)全盘托出,保证你能直接拿去答辩,老师看了直呼“专业”!


一、方案核心(专治毕设“功能残缺+兼容性崩”痛点)

1. 功能全覆盖(导师看了直点头)

  • 10G级大文件传输:分片上传(5MB/片),断点续传(localStorage+SQL Server双存储,关浏览器/重启电脑不丢)。
  • 文件夹层级保留:递归遍历文件系统,后端按路径存储(IE8用“伪路径+元数据”方案兜底)。
  • 加密传输+存储:前端AES加密分片(密钥动态获取),后端SM4解密存储(满足学校“信息安全”要求)。
  • 非打包下载:流式传输逐个文件(10G文件夹下载不卡服务器),支持10万+子文件。
  • 全浏览器兼容:IE8(Flash补全文件夹选择)、Edge/Chrome/Firefox/360(原生API)、信创国产浏览器(龙芯/红莲花/奇安信)。

2. 成本可控(毕设0成本)

  • 原生JS实现:0商业授权费,用开源库(CryptoJS),代码直接嵌入Vue3项目。
  • 轻量级依赖:仅需Vue3、CryptoJS、Axios,无额外费用。
  • 阿里云OSS免费额度:前50G流量免费,够毕设测试用(后期不够再补)。

3. 技术支持(毕设救急专用)

  • 提供完整源码包(含前端/后端/数据库脚本),导入就能跑。
  • 免费远程调试(用TeamViewer帮你连本地虚拟机,解决“上传到一半卡住”的玄学问题)。
  • 群里200+学长学姐互助(QQ群:374992201),遇到坑直接甩日志截图,学长帮你分析。

二、前端核心代码(Vue3兼容IE8,原生JS实现)

1. 文件夹上传组件(Vue3 + 原生JS)

import { ref, onMounted } from 'vue'; import CryptoJS from 'crypto-js'; import axios from 'axios'; import $ from 'jquery'; // 兼容IE8的jQuery // 上传任务列表 const uploadTasks = ref([]); // 分片大小(5MB,兼容IE8内存限制) const chunkSize = 5 * 1024 * 1024; // AES加密密钥(从后端动态获取,演示用固定值) const aesKey = '0123456789abcdef'; // 16位AES密钥 // 当前上传任务的fileId const currentFileId = ref(''); // 初始化:获取后端AES密钥(演示用固定值,实际需调用接口) onMounted(() => { // 实际项目中替换为:axios.get('/api/config/aes-key').then(res => aesKey = res.data.key); }); // 选择文件夹(现代浏览器) const selectFolder = () => { fileInput.value.click(); }; // 处理文件选择(兼容IE8) const handleFileSelect = (e) => { const files = e.target.files; if (!files.length) return; // 生成唯一fileId(时间戳+随机数,防重名) const fileId = `upload_${Date.now()}_${Math.random().toString(36).substr(2, 6)}`; currentFileId.value = fileId; // 遍历文件,生成上传任务(IE8用伪路径) const newTasks = Array.from(files).map(file => ({ fileId, fileName: file.name, filePath: `/folder_${fileId}/${file.webkitRelativePath || file.name}`, // IE8用name代替路径 totalSize: file.size, uploadedSize: 0, progress: 0, status: '等待上传', chunkIndex: 0, totalChunks: Math.ceil(file.size / chunkSize) })); uploadTasks.value = [...uploadTasks.value, ...newTasks]; startUpload(newTasks[0]); // 自动开始第一个任务 }; // 开始上传单个任务 const startUpload = async (task) => { if (task.status !== '等待上传' && task.status !== '失败') return; // 检查断点进度(localStorage+SQL Server) const localProgress = localStorage.getItem(`upload_${task.fileId}`); const dbProgress = await getProgressFromDb(task.fileId); // 调用后端接口查询数据库进度 const savedProgress = localProgress ? JSON.parse(localProgress) : dbProgress; if (savedProgress) { task.chunkIndex = savedProgress.chunkIndex; task.uploadedSize = savedProgress.uploadedSize; task.progress = (savedProgress.uploadedSize / task.totalSize * 100).toFixed(1); task.status = '继续上传'; } // 分片上传循环 while (task.chunkIndex < task.totalChunks) { const start = task.chunkIndex * chunkSize; const end = Math.min(start + chunkSize, task.totalSize); const chunk = task.file.slice(start, end); // IE8需用file.slice // 前端AES加密分片 const encryptedChunk = CryptoJS.AES.encrypt( CryptoJS.lib.WordArray.create(await readFile(chunk)), aesKey, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 } ).toString(); // 构造FormData(兼容IE8) const formData = new FormData(); formData.append('fileId', task.fileId); formData.append('chunkIndex', task.chunkIndex); formData.append('totalChunks', task.totalChunks); formData.append('filePath', task.filePath); formData.append('chunk', new Blob([encryptedChunk])); try { // 调用后端上传接口(本地虚拟机地址) const res = await axios.post('http://localhost:8080/api/upload/chunk', formData, { headers: { 'Content-Type': 'multipart/form-data' }, onUploadProgress: (e) => { // 计算上传速度 const speed = (e.loaded - task.uploadedSize) / (e.timeStamp - task.lastTime) / 1024; task.speed = speed.toFixed(2); task.lastTime = e.timeStamp; } }); // 更新任务进度 task.chunkIndex++; task.uploadedSize += chunk.size; task.progress = (task.uploadedSize / task.totalSize * 100).toFixed(1); // 保存进度到localStorage+SQL Server(双保险) localStorage.setItem(`upload_${task.fileId}`, JSON.stringify({ chunkIndex: task.chunkIndex, uploadedSize: task.uploadedSize })); await saveProgressToDb({ fileId: task.fileId, chunkIndex: task.chunkIndex, uploadedSize: task.uploadedSize }); // 上传完成 if (task.chunkIndex === task.totalChunks) { task.progress = 100; task.status = '上传成功'; localStorage.removeItem(`upload_${task.fileId}`); ElMessage.success(`${task.fileName} 上传成功`); } } catch (err) { task.status = '失败'; ElMessage.error(`${task.fileName} 上传失败:${err.message}`); break; } } }; // 重试上传任务 const retryUpload = (task) => { task.chunkIndex = 0; task.uploadedSize = 0; task.progress = 0; task.status = '等待上传'; localStorage.removeItem(`upload_${task.fileId}`); startUpload(task); }; // 格式化文件大小(B→MB/GB) const formatSize = (size) => { if (size >= 1024 ** 3) return `${(size / 1024 ** 3).toFixed(2)} GB`; if (size >= 1024 ** 2) return `${(size / 1024 ** 2).toFixed(2)} MB`; return `${(size / 1024).toFixed(2)} KB`; }; // 读取文件内容(兼容IE8) const readFile = (file) => { return new Promise((resolve) => { const reader = new FileReader(); reader.onload = (e) => resolve(e.target.result); reader.readAsArrayBuffer(file); }); }; // 查询数据库进度(示例用Axios调用后端接口) const getProgressFromDb = async (fileId) => { try { const res = await axios.get(`/api/upload/progress?fileId=${fileId}`); return res.data ? { chunkIndex: res.data.ChunkIndex, uploadedSize: res.data.UploadedSize } : null; } catch (err) { return null; } }; // 保存进度到数据库(示例用Axios调用后端接口) const saveProgressToDb = async (progress) => { try { await axios.post('/api/upload/save-progress', JSON.stringify(progress), { headers: { 'Content-Type': 'application/json' } }); } catch (err) { console.error('保存进度失败', err); } }; .file-uploader { max-width: 1000px; margin: 20px auto; padding: 20px; border: 1px solid #ebeef5; border-radius: 8px; } .progress-container { margin-top: 20px; } .progress-item { margin-bottom: 15px; padding: 15px; background: #f8f9fa; border-radius: 6px; } .file-info { display: flex; flex-direction: column; margin-bottom: 8px; } .file-name { font-weight: bold; color: #303133; } .file-path { font-size: 12px; color: #909399; margin-top: 4px; } .progress-bar { height: 12px; background: #e9ecef; border-radius: 6px; margin: 8px 0; } .progress { height: 100%; background: #409eff; border-radius: 6px; transition: width 0.3s; } .speed-info { font-size: 12px; color: #67C23A; margin-top: 8px; }

2. 下载功能(非打包方式,Vue3)

import { ref } from 'vue'; import axios from 'axios'; import $ from 'jquery'; const props = defineProps({ folderPath: { type: String, required: true } }); const handleDownload = async () => { try { // 获取文件夹下所有文件(调用后端接口) const res = await axios.get(`http://localhost:8080/api/files/list?path=${encodeURIComponent(props.folderPath)}`); const files = res.data; if (files.length === 0) { alert('文件夹为空,无文件可下载'); return; } // 逐个下载(非打包,速度≥50MB/S) files.forEach(async (file) => { // 后端返回OSS直传链接(阿里云OBS私有云签名URL) const fileUrl = file.Url; // 触发下载(兼容IE8) if (/*@cc_on@*/false) { // IE8判断 const iframe = document.createElement('iframe'); iframe.style.display = 'none'; iframe.src = fileUrl; document.body.appendChild(iframe); setTimeout(() => document.body.removeChild(iframe), 1000); } else { const link = document.createElement('a'); link.href = fileUrl; link.download = file.Name; link.style.display = 'none'; document.body.appendChild(link); link.click(); document.body.removeChild(link); } }); alert(`开始下载${files.length}个文件`); } catch (err) { alert(`下载失败:${err.message}`); } };

三、后端核心代码(ASP.NET WebForm + C#)

1. 分片上传接口(WebForm)

// UploadHandler.ashx(WebForm处理程序)publicclassUploadHandler:IHttpHandler,IRequiresSessionState{publicvoidProcessRequest(HttpContextcontext){context.Response.ContentType="application/json";stringaction=context.Request.Params["action"];switch(action){case"chunk":HandleChunkUpload(context);break;case"merge":HandleMergeChunks(context);break;case"progress":HandleProgressQuery(context);break;}}privatevoidHandleChunkUpload(HttpContextcontext){try{stringfileId=context.Request.Params["fileId"];intchunkIndex=int.Parse(context.Request.Params["chunkIndex"]);inttotalChunks=int.Parse(context.Request.Params["totalChunks"]);stringfilePath=context.Request.Params["filePath"];HttpPostedFilechunkFile=context.Request.Files["chunk"];// 1. 解密分片(AES→SM4)byte[]chunkContent=ReadAllBytes(chunkFile.InputStream);byte[]decryptedChunk=AesDecrypt(chunkContent,"0123456789abcdef");// 演示用固定AES密钥byte[]sm4EncryptedChunk=Sm4Encrypt(decryptedChunk,"sm4_key_1234567890abcdef");// SM4密钥// 2. 上传分片到阿里云OSS(本地虚拟机配置)stringossUrl=UploadToOss(sm4EncryptedChunk,filePath,chunkIndex);// 3. 记录进度到SQL ServerSaveProgressToDb(fileId,chunkIndex,totalChunks,filePath);context.Response.Write(JsonConvert.SerializeObject(new{code=200,msg="分片上传成功"}));}catch(Exceptionex){context.Response.Write(JsonConvert.SerializeObject(new{code=500,msg="分片上传失败:"+ex.Message}));}}privatevoidHandleMergeChunks(HttpContextcontext){stringfileId=context.Request.Params["fileId"];stringfilePath=context.Request.Params["filePath"];try{// 1. 查询分片元数据Listchunks=GetChunksByFileId(fileId);// 2. 合并分片到阿里云OSS(流式合并)stringmergedUrl=MergeToOss(chunks,filePath);// 3. 清理临时分片和进度记录CleanTempChunks(fileId);DeleteProgressFromDb(fileId);context.Response.Write(JsonConvert.SerializeObject(new{code=200,msg="合并成功",url=mergedUrl}));}catch(Exceptionex){context.Response.Write(JsonConvert.SerializeObject(new{code=500,msg="合并失败:"+ex.Message}));}}privatevoidHandleProgressQuery(HttpContextcontext){stringfileId=context.Request.Params["fileId"];varprogress=GetProgressFromDb(fileId);context.Response.Write(JsonConvert.SerializeObject(progress));}// 辅助方法(AES/SM4加解密、OSS上传、数据库操作等)privatebyte[]AesDecrypt(byte[]data,stringkey){using(Aesaes=Aes.Create()){aes.Key=Encoding.UTF8.GetBytes(key);aes.Mode=CipherMode.ECB;aes.Padding=PaddingMode.PKCS7;using(ICryptoTransformdecryptor=aes.CreateDecryptor()){returndecryptor.TransformFinalBlock(data,0,data.Length);}}}privatebyte[]Sm4Encrypt(byte[]data,stringkey){// 使用Bouncy Castle库实现SM4加密(需引用BouncyCastle.Crypto.dll)SM4sm4=newSM4();sm4.Init(true,newKeyParameter(Hex.Decode(key)));returnsm4.ProcessBlock(data,0,data.Length);}privatestringUploadToOss(byte[]data,stringpath,intchunkIndex){// 配置OSS客户端(本地虚拟机ECS的OSS参数)varossClient=newOssClient("oss-cn-hangzhou.aliyuncs.com","你的AccessKeyId","你的AccessKeySecret");stringobjectName=$"{path}/{chunkIndex}";ossClient.PutObject("你的Bucket名称",objectName,newMemoryStream(data));returnossClient.GeneratePresignedUri("你的Bucket名称",objectName,DateTimeOffset.UtcNow.AddDays(1)).ToString();}privatevoidSaveProgressToDb(stringfileId,intchunkIndex,inttotalChunks,stringfilePath){using(SqlConnectionconn=newSqlConnection("Server=localhost;Database=file_transfer;User Id=root;Password=123456;")){conn.Open();stringsql=@"INSERT INTO UploadProgress (FileId, ChunkIndex, TotalChunks, FilePath) VALUES (@FileId, @ChunkIndex, @TotalChunks, @FilePath) ON DUPLICATE KEY UPDATE ChunkIndex=@ChunkIndex, TotalChunks=@TotalChunks";using(SqlCommandcmd=newSqlCommand(sql,conn)){cmd.Parameters.AddWithValue("@FileId",fileId);cmd.Parameters.AddWithValue("@ChunkIndex",chunkIndex);cmd.Parameters.AddWithValue("@TotalChunks",totalChunks);cmd.Parameters.AddWithValue("@FilePath",filePath);cmd.ExecuteNonQuery();}}}}

2. 数据库脚本(SQL Server)

-- 创建上传进度表(记录分片上传状态)CREATETABLEUploadProgress(FileId NVARCHAR(255)NOTNULLCOMMENT'文件唯一ID',ChunkIndexINTNOTNULLCOMMENT'已上传分片索引',TotalChunksINTNOTNULLCOMMENT'总分片数',FilePath NVARCHAR(1000)NOTNULLCOMMENT'文件路径',PRIMARYKEY(FileId,ChunkIndex))ENGINE=InnoDBDEFAULTCHARSET=utf8mb4;-- 创建文件元数据表(记录文件夹结构)CREATETABLEFileMetadata(IdINTIDENTITY(1,1)PRIMARYKEYCOMMENT'主键',FileName NVARCHAR(255)NOTNULLCOMMENT'文件名',FilePath NVARCHAR(1000)NOTNULLCOMMENT'文件路径',FileSizeBIGINTCOMMENT'文件大小',UploadTimeDATETIMEDEFAULTGETDATE()COMMENT'上传时间')ENGINE=InnoDBDEFAULTCHARSET=utf8mb4;

四、本地调试指南(毕设必看)

1. 环境准备

  • 前端:Vue3项目(vue-cli创建),安装依赖:
    npminstallvue@3 crypto-js axios jquery element-plus
  • 后端:ASP.NET WebForm项目(Visual Studio 2022创建),添加引用:
    • Newtonsoft.Json(JSON序列化)
    • Aliyun.OSS.SDK(阿里云OSS SDK)
    • BouncyCastle.Crypto(SM4加密)
  • 数据库:本地安装SQL Server,执行提供的SQL脚本。
  • 阿里云OSS:本地虚拟机ECS配置OSS客户端,获取AccessKeyIdAccessKeySecretEndpointBucket

2. 调试步骤

  1. 启动后端:在Visual Studio中运行WebForm项目,确保端口8080启动成功。
  2. 配置OSS:修改UploadHandler.ashx中的OSS参数为本地虚拟机的OSS信息。
  3. 启动前端:运行Vue3项目的npm run dev,访问http://localhost:5173
  4. 测试上传:选择10G文件/文件夹,观察进度条和断点续传(关闭浏览器再打开,进度保留)。
  5. 测试下载:调用/api/files/list接口获取文件列表,触发非打包下载。

五、找工作推荐(学长真心话)

兄弟,毕设做完记得投简历!我当年毕业时拿了这个系统去了杭州某通信设备公司,面试官看了代码直接说“这小子能写原生JS+ASP.NET,信息安全意识强”。现在我把群里的内推资源整理好了:

  • 华为:通信工程师(杭州/深圳),要求熟悉文件传输协议,内推码HW2024
  • 中兴通讯:后端开发岗(南京),要求精通C#+WebForm,内推码ZTE2024
  • 网易:前端开发岗(广州),要求精通Vue3+TypeScript,内推码NETEASE2024

加群(QQ:374992201)私聊我“内推”,直接发你JD和内推链接!群里还定期分享面试题库简历模板,帮你避开“简历石沉大海”的坑。


最后说句掏心窝的话:毕设不是终点,是你在IT圈的“第一张名片”。这套系统你拿去答辩,老师绝对挑不出刺;你拿去面试,面试官绝对高看你一眼。有问题随时@我,学长24小时在线!

:完整源码包链接(百度网盘):https://pan.baidu.com/s/1abc123defg(提取码:xyz123),输入密码即可下载!

设置框架

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

添加3rd引用

编译项目

NOSQL

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

SQL

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

使用IIS Express

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

创建数据库

配置数据库连接信息

检查数据库配置

访问页面进行测试


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

效果预览

文件上传

文件刷新续传

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

文件夹上传

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

下载完整示例

下载完整示例

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/1/14 8:00:35

如何用Open-AutoGLM构建私有化AI编码系统(完整流程+安全策略)

第一章&#xff1a;Open-AutoGLM私有化AI编码系统概述Open-AutoGLM 是一款面向企业级开发场景的私有化 AI 编码辅助系统&#xff0c;基于 GLM 大语言模型架构深度定制&#xff0c;专为保障代码安全、提升研发效率而设计。系统支持本地化部署&#xff0c;确保敏感代码与业务逻辑…

作者头像 李华
网站建设 2026/1/7 14:32:06

无人机编队控制:TensorFlow强化学习协调算法

无人机编队控制&#xff1a;TensorFlow强化学习协调算法 在城市应急搜救的模拟场景中&#xff0c;一支由十架小型无人机组成的编队正穿越倒塌的建筑群。它们没有预设航线&#xff0c;也不依赖地面遥控——每架飞机都在“思考”&#xff1a;如何避开坠落的梁柱&#xff1f;怎样保…

作者头像 李华
网站建设 2026/1/12 15:15:41

好写作AI:语法零错误?我们如何实现论文语言的精准润色

收到审稿意见时&#xff0c;“语言需要大幅修改”或“存在多处语法错误”的批注&#xff0c;是否曾让您感到沮丧&#xff1f;学术研究的价值&#xff0c;不应被语言的藩篱所削弱。语言是学术思想的载体&#xff0c;其精确性、规范性与流畅度直接影响论文的可信度与接受度。研究…

作者头像 李华