网站备案需要ftp吗,个人做电商网站需要备案吗,千图网官网素材,松江建设网站公司大文件上传系统开发指南#xff08;兼容IE8的WebUploader实现#xff09;
项目概述
大家好#xff0c;我是广东的一名.NET程序员#xff0c;最近接了一个让人头大的外包项目。客户要求实现一个支持20G大文件上传的系统#xff0c;还要兼容IE8这种古董浏览器#xff0c;…大文件上传系统开发指南兼容IE8的WebUploader实现项目概述大家好我是广东的一名.NET程序员最近接了一个让人头大的外包项目。客户要求实现一个支持20G大文件上传的系统还要兼容IE8这种古董浏览器预算只有100元不过没关系咱们程序员最擅长的就是在有限条件下创造无限可能。技术选型分析经过深思熟虑我决定采用以下技术方案前端WebUploader兼容IE8 原生JavaScript因为客户要求不能用jQuery等库后端ASP.NET WebForm客户指定的技术栈存储阿里云OSS私有云存储数据库SQL Server记录文件元数据系统功能清单大文件上传支持20G文件夹上传保留层级结构文件下载非打包方式加密传输SM4/AES断点续传网页关闭后恢复兼容IE8及所有主流浏览器文件夹下载保留结构前端实现代码HTML部分 (index.html)大文件上传系统 - 兼容IE8 body { font-family: Arial, sans-serif; margin: 20px; } .uploader-container { margin: 20px 0; } .file-list { margin-top: 20px; border: 1px solid #ddd; padding: 10px; } .progress { height: 20px; margin: 5px 0; } .ie-warning { color: red; background: #ffeeee; padding: 10px; display: none; } 大文件上传系统 您正在使用旧版浏览器部分功能可能受限建议升级到现代浏览器。 选择文件/文件夹 开始上传JavaScript部分 (app.js)// 检测IE8并显示警告functioncheckIE(){varuawindow.navigator.userAgent;varmsieua.indexOf(MSIE );if(msie0||!!navigator.userAgent.match(/Trident.*rv:11\./)){varversionparseInt(ua.substring(msie5,ua.indexOf(.,msie)));if(version8){document.getElementById(ie-warning).style.displayblock;}}}// 主上传逻辑(function(){checkIE();// 配置项varconfig{chunkSize:5*1024*1024,// 5MB分片threads:3,// 并发数server:/FileUpload.ashx,// 上传处理地址encryptKey:12345678901234567890123456789012// AES加密密钥(32位)};// 初始化WebUploadervaruploaderWebUploader.create({swf://cdn.jsdelivr.net/npm/webuploader0.1.1/dist/Uploader.swf,server:config.server,pick:#filePicker,resize:false,chunked:true,chunkSize:config.chunkSize,threads:config.threads,formData:{// 可以在这里添加额外的表单数据},compress:false,duplicate:true,accept:null,// 允许文件夹上传通过webkitdirectory属性directory:true});// 文件加入队列事件uploader.on(fileQueued,function(file){// 如果是文件夹记录相对路径if(file._relativePath){file.relativePathfile._relativePath;}// 创建文件项UIvar$listdocument.getElementById(fileList);var$itemdocument.createElement(div);$item.idfile.id;$item.innerHTML file.name (WebUploader.formatSize(file.size)) 等待上传... ;$list.appendChild($item);});// 上传进度事件uploader.on(uploadProgress,function(file,percentage){var$itemdocument.getElementById(file.id);var$progress$item.querySelector(.progress);$progress.style.width(percentage*100)%;var$status$item.querySelector(.filestatus);$status.innerHTML上传中 Math.round(percentage*100)%;});// 上传成功事件uploader.on(uploadSuccess,function(file,response){var$itemdocument.getElementById(file.id);var$status$item.querySelector(.filestatus);if(response.success){$status.innerHTML上传成功;$item.querySelector(.progress).style.background#5cb85c;}else{$status.innerHTML上传失败: response.message;$item.querySelector(.progress).style.background#d9534f;}});// 上传错误事件uploader.on(uploadError,function(file,reason){var$itemdocument.getElementById(file.id);var$status$item.querySelector(.filestatus);$status.innerHTML上传错误: reason;$item.querySelector(.progress).style.background#d9534f;});// 开始上传按钮document.getElementById(ctlBtn).addEventListener(click,function(){uploader.upload();});// 加密函数 (AES)functionencryptData(data){// 在实际应用中应该使用更安全的密钥管理方式varkeyCryptoJS.enc.Utf8.parse(config.encryptKey);varivCryptoJS.enc.Utf8.parse(config.encryptKey.substring(0,16));// 如果是文件对象我们实际上是在上传时对分片进行加密// 这里演示对字符串加密if(typeofdatastring){varencryptedCryptoJS.AES.encrypt(data,key,{iv:iv,mode:CryptoJS.mode.CBC,padding:CryptoJS.pad.Pkcs7});returnencrypted.toString();}returndata;}// 在上传前对文件分片进行加密简化版// 实际项目中应该在服务端进行加密这里只是演示uploader.on(uploadBeforeSend,function(block,data){// 可以在这里对block.blob进行加密处理// 但由于浏览器端加密大文件性能较差建议仅作演示// 实际项目中应该使用HTTPS 服务端加密});// 断点续传支持// 通过WebUploader的chunked和uniqueIdentifier实现// 服务端需要配合记录上传进度// 文件夹下载功能将在服务端实现})();后端实现代码 (ASP.NET WebForm)文件上传处理 (FileUpload.ashx)%WebHandlerLanguageC#ClassFileUpload%usingSystem;usingSystem.Web;usingSystem.IO;usingSystem.Data.SqlClient;usingSystem.Security.Cryptography;usingSystem.Text;usingAliyun.OSS;publicclassFileUpload:IHttpHandler{// 配置信息实际项目中应该放在配置文件中privatestaticreadonlystringOssEndpointyour-oss-endpoint;privatestaticreadonlystringOssAccessKeyIdyour-access-key-id;privatestaticreadonlystringOssAccessKeySecretyour-access-key-secret;privatestaticreadonlystringOssBucketNameyour-bucket-name;privatestaticreadonlystringConnStringyour-sqlserver-connection-string;publicvoidProcessRequest(HttpContextcontext){context.Response.ContentTypeapplication/json;try{// 检查请求方法if(context.Request.HttpMethod!POST){context.Response.Write({\success\:false,\message\:\只支持POST请求\});return;}// 获取上传参数varfilecontext.Request.Files[file];varchunkcontext.Request[chunk]!null?int.Parse(context.Request[chunk]):0;varchunkscontext.Request[chunks]!null?int.Parse(context.Request[chunks]):1;varfileIdcontext.Request[fileId]??Guid.NewGuid().ToString();varfileNamecontext.Request[name]??file.FileName;varrelativePathcontext.Request[relativePath]??;varfileSizecontext.Request[totalSize]!null?long.Parse(context.Request[totalSize]):file.ContentLength;// 创建OSS客户端varclientnewOssClient(OssEndpoint,OssAccessKeyId,OssAccessKeySecret);// 临时文件路径分片存储vartempFolderPath.Combine(context.Server.MapPath(~/App_Data/UploadTemp),fileId);if(!Directory.Exists(tempFolder)){Directory.CreateDirectory(tempFolder);}vartempFilePathPath.Combine(tempFolder,chunk.ToString());// 保存分片using(varfsnewFileStream(tempFilePath,FileMode.Create)){// 这里可以添加解密逻辑如果前端加密了// 实际项目中建议使用HTTPS 服务端加密file.InputStream.CopyTo(fs);}// 如果是最后一个分片合并文件if(chunkchunks-1){varfinalFilePathPath.Combine(context.Server.MapPath(~/App_Data/UploadFiles),Guid.NewGuid().ToString()Path.GetExtension(fileName));// 合并文件MergeFile(tempFolder,finalFilePath,chunks);// 上传到OSSvarossKeyPath.Combine(DateTime.Now.ToString(yyyyMMdd),Guid.NewGuid().ToString()Path.GetExtension(fileName));using(varfsnewFileStream(finalFilePath,FileMode.Open)){// 这里可以添加加密逻辑使用SM4或AES// 示例使用AES加密实际项目中应该使用更安全的密钥管理varencryptedDataEncryptStream(fs,your-encryption-key-32-chars-long);// 重置流位置encryptedData.Position0;// 上传到OSSclient.PutObject(OssBucketName,ossKey,encryptedData);}// 删除临时文件Directory.Delete(tempFolder,true);File.Delete(finalFilePath);// 记录文件元数据到数据库RecordFileMetadata(fileName,relativePath,fileSize,ossKey);context.Response.Write(string.Format({{\success\:true,\fileId\:\{0}\,\ossKey\:\{1}\}},fileId,ossKey));}else{context.Response.Write(string.Format({{\success\:true,\fileId\:\{0}\,\chunk\:{1}}},fileId,chunk));}}catch(Exceptionex){context.Response.Write(string.Format({{\success\:false,\message\:\{0}\}},ex.Message.Replace(\,\\\)));}}// 合并分片文件privatevoidMergeFile(stringtempFolder,stringfinalFilePath,intchunks){using(varfsnewFileStream(finalFilePath,FileMode.Create)){for(vari0;ichunks;i){varchunkPathPath.Combine(tempFolder,i.ToString());using(varchunkStreamnewFileStream(chunkPath,FileMode.Open)){chunkStream.CopyTo(fs);}File.Delete(chunkPath);}}}// 加密流简化版实际项目中使用更安全的实现privateMemoryStreamEncryptStream(Streaminput,stringkey){// 读取整个流到内存仅用于演示大文件应该使用流式加密using(varmsnewMemoryStream()){input.CopyTo(ms);vardatams.ToArray();// 使用AES加密using(varaesAes.Create()){aes.KeyEncoding.UTF8.GetBytes(key.PadRight(32).Substring(0,32));aes.IVEncoding.UTF8.GetBytes(key.PadRight(16).Substring(0,16));using(varencryptoraes.CreateEncryptor())using(varresultStreamnewMemoryStream()){using(varcryptoStreamnewCryptoStream(resultStream,encryptor,CryptoStreamMode.Write)){cryptoStream.Write(data,0,data.Length);}returnresultStream;}}}}// 记录文件元数据到数据库privatevoidRecordFileMetadata(stringfileName,stringrelativePath,longsize,stringossKey){using(varconnnewSqlConnection(ConnString)){conn.Open();varcmdnewSqlCommand(INSERT INTO FileMetadata (FileName, RelativePath, FileSize, OssKey, UploadTime) VALUES (FileName, RelativePath, FileSize, OssKey, UploadTime),conn);cmd.Parameters.AddWithValue(FileName,fileName);cmd.Parameters.AddWithValue(RelativePath,relativePath);cmd.Parameters.AddWithValue(FileSize,size);cmd.Parameters.AddWithValue(OssKey,ossKey);cmd.Parameters.AddWithValue(UploadTime,DateTime.Now);cmd.ExecuteNonQuery();}}publicboolIsReusable{get{returnfalse;}}}文件下载处理 (FileDownload.ashx)%WebHandlerLanguageC#ClassFileDownload%usingSystem;usingSystem.Web;usingSystem.IO;usingSystem.Data.SqlClient;usingSystem.Security.Cryptography;usingSystem.Text;usingAliyun.OSS;publicclassFileDownload:IHttpHandler{// 配置信息同上传处理privatestaticreadonlystringOssEndpointyour-oss-endpoint;privatestaticreadonlystringOssAccessKeyIdyour-access-key-id;privatestaticreadonlystringOssAccessKeySecretyour-access-key-secret;privatestaticreadonlystringOssBucketNameyour-bucket-name;privatestaticreadonlystringConnStringyour-sqlserver-connection-string;publicvoidProcessRequest(HttpContextcontext){try{varossKeycontext.Request[ossKey];varisFoldercontext.Request[isFolder]true;if(string.IsNullOrEmpty(ossKey)){thrownewException(缺少ossKey参数);}// 创建OSS客户端varclientnewOssClient(OssEndpoint,OssAccessKeyId,OssAccessKeySecret);if(isFolder){// 文件夹下载 - 查询所有子文件varfileListGetFolderFiles(ossKey);// 设置响应头实际项目中应该使用ZIP打包下载但客户要求非打包方式// 这里改为返回文件列表由前端逐个下载context.Response.ContentTypeapplication/json;context.Response.Write(Newtonsoft.Json.JsonConvert.SerializeObject(fileList));}else{// 单个文件下载varfileMetaGetFileMetadata(ossKey);if(fileMetanull){thrownewException(文件不存在);}// 从OSS获取文件对象varobjclient.GetObject(OssBucketName,ossKey);// 设置响应头context.Response.ContentTypeapplication/octet-stream;context.Response.AddHeader(Content-Disposition,attachment; filenameHttpUtility.UrlEncode(fileMeta.FileName));// 解密并输出文件内容using(varossStreamobj.Content){// 这里可以添加解密逻辑与上传时的加密对应// 示例使用AES解密vardecryptedStreamDecryptStream(ossStream,your-encryption-key-32-chars-long);// 输出到响应流decryptedStream.CopyTo(context.Response.OutputStream);}}}catch(Exceptionex){context.Response.ContentTypeapplication/json;context.Response.Write(string.Format({{\success\:false,\message\:\{0}\}},ex.Message.Replace(\,\\\)));}}// 解密流与上传时的加密对应privateStreamDecryptStream(Streaminput,stringkey){// 读取整个流到内存仅用于演示大文件应该使用流式解密using(varmsnewMemoryStream()){input.CopyTo(ms);vardatams.ToArray();// 使用AES解密using(varaesAes.Create()){aes.KeyEncoding.UTF8.GetBytes(key.PadRight(32).Substring(0,32));aes.IVEncoding.UTF8.GetBytes(key.PadRight(16).Substring(0,16));using(vardecryptoraes.CreateDecryptor())using(varresultStreamnewMemoryStream()){using(varcryptoStreamnewCryptoStream(resultStream,decryptor,CryptoStreamMode.Write)){cryptoStream.Write(data,0,data.Length);}returnresultStream;}}}}// 获取文件夹下的所有文件privateobjectGetFolderFiles(stringfolderPath){// 实际项目中应该查询数据库获取文件夹结构// 这里简化处理返回模拟数据returnnew{successtrue,filesnew[]{new{namefile1.txt,size1024,ossKeypath/to/file1.txt},new{namesubfolder/file2.txt,size2048,ossKeypath/to/subfolder/file2.txt}}};}// 获取文件元数据privatedynamicGetFileMetadata(stringossKey){using(varconnnewSqlConnection(ConnString)){conn.Open();varcmdnewSqlCommand(SELECT FileName, RelativePath, FileSize FROM FileMetadata WHERE OssKey OssKey,conn);cmd.Parameters.AddWithValue(OssKey,ossKey);using(varreadercmd.ExecuteReader()){if(reader.Read()){returnnew{FileNamereader[FileName].ToString(),RelativePathreader[RelativePath].ToString(),FileSizeConvert.ToInt64(reader[FileSize])};}}}returnnull;}publicboolIsReusable{get{returnfalse;}}}数据库表设计-- 文件元数据表CREATETABLEFileMetadata(IdINTIDENTITY(1,1)PRIMARYKEY,FileName NVARCHAR(255)NOTNULL,RelativePath NVARCHAR(MAX)NULL,FileSizeBIGINTNOTNULL,OssKey NVARCHAR(500)NOTNULL,UploadTimeDATETIMENOTNULL,IsDeletedBITDEFAULT0,DeleteTimeDATETIMENULL);-- 上传进度记录表用于断点续传CREATETABLEUploadProgress(IdINTIDENTITY(1,1)PRIMARYKEY,FileId NVARCHAR(50)NOTNULL,FileName NVARCHAR(255)NOTNULL,TotalSizeBIGINTNOTNULL,UploadedSizeBIGINTNOTNULL,ChunkCountINTNOTNULL,UploadedChunks NVARCHAR(MAX)NULL,-- JSON格式存储已上传的分片索引LastUpdateDATETIMENOTNULL,IsCompletedBITDEFAULT0);项目部署说明环境准备Windows Server IIS.NET Framework 4.5SQL Server 2012阿里云OSS账号部署步骤在IIS中创建网站指向项目目录配置应用程序池为.NET 4.0修改Web.config中的数据库连接字符串配置OSS访问密钥确保App_Data目录有写入权限兼容性测试在IE8及其他浏览器中测试上传下载功能测试大文件5GB上传测试文件夹上传包含多级子目录注意事项加密实现示例中的加密是简化版实际项目应该使用HTTPS传输服务端使用更安全的密钥管理考虑使用国密SM4算法需要引入相关库性能优化对于超大文件考虑使用流式加密而非内存加密可以使用异步处理提高服务器响应能力断点续传示例中简化了实现实际项目应该在数据库中记录上传进度处理各种异常情况网络中断、服务器重启等文件夹下载客户要求非打包下载实际实现中可以返回文件列表由前端逐个下载或者使用服务端生成临时链接的方式总结这个项目虽然预算有限只有100元但通过合理的技术选型和代码实现我们还是能够满足客户的需求。关键点在于使用WebUploader实现兼容IE8的文件上传通过分片上传支持大文件利用OSS存储解决服务器空间问题数据库记录元数据支持断点续传虽然代码中有些简化处理如加密但整体架构是完整的你可以根据实际需求进行扩展。最后欢迎加入我们的QQ群374992201交流技术一起接单赚钱群里经常有红包和项目分享说不定下一个10万项目就在群里等着你呢导入项目导入到Eclipse点南查看教程导入到IDEA点击查看教程springboot统一配置点击查看教程工程NOSQLNOSQL示例不需要任何配置可以直接访问测试创建数据表选择对应的数据表脚本这里以SQL为例修改数据库连接信息访问页面进行测试文件存储路径up6/upload/年/月/日/guid/filename效果预览文件上传文件刷新续传支持离线保存文件进度在关闭浏览器刷新浏览器后进行不丢失仍然能够继续上传文件夹上传支持上传文件夹并保留层级结构同样支持进度信息离线保存刷新页面关闭页面重启系统不丢失上传进度。下载示例点击下载完整示例