OpenClaw不是QQ机器人,而是服务编排型消息总线
2026/6/24 16:50:06
大家好,我是老王,一个在西安写了15年Java的老程序员。最近接了个外包项目,需求简单概括就是:“用IE9上传20G文件夹,预算100块还要7×24小时支持”——这感觉就像是让我用自行车送外卖,还要求时速120公里!
甲方:“老王啊,我们要做个文件上传系统…”
我:“没问题,这个我熟!”
甲方:“要支持20G文件夹上传,保留层级结构,要加密…”
我:“小case!”
甲方:“预算100块包干,含源码文档和技术支持…”
我:“老板,我突然想起我家煤气灶还没关…”
// 预算检测工具类publicclassBudgetValidator{publicstaticvoidcheck(doublebudget){if(budget<10000){thrownewInsufficientBudgetException("您的预算仅够买"+(int)(budget/3)+"杯蜜雪冰城");}}}// IE9文件夹上传核心代码functionhandleIEFolderUpload(files){if(!files){alert('请使用Chrome浏览器以获得更好体验(或者加钱)');return;}letfileCount=0;for(leti=0;i<files.length;i++){constfile=files[i];// 假装处理了文件夹结构constfakePath=file.name.replace(/\\/g,'/');uploadFile(file,fakePath);fileCount++;}console.log(`成功上传了${fileCount}个文件(可能丢失了层级关系)`);}// 文件信息实体(丐版)@EntitypublicclassFileInfo{@IdprivateStringid;privateStringfileName;privateStringfilePath;// 例如 "/root/folder1/file.txt"privateLongfileSize;privateBooleanisDirectory;// 省去getter/setter...}// 上传控制器(简化版)@RestController@RequestMapping("/api/upload")publicclassUploadController{@PostMappingpublicStringupload(@RequestParamMultipartFilefile,@RequestParamStringrelativePath){// 1. 加密存储(假装很安全)byte[]encrypted=encrypt(file.getBytes());// 2. 保存到阿里云OSSStringossPath="user_uploads/"+UUID.randomUUID();ossClient.putObject(bucketName,ossPath,newByteArrayInputStream(encrypted));// 3. 记录文件结构FileInfofileInfo=newFileInfo();fileInfo.setFilePath(relativePath);fileRepository.save(fileInfo);return"success";}privatebyte[]encrypt(byte[]data){// 这里应该用AES/SM4,但预算只够写个伪代码returndata;// 假装加密了}}// 递归读取文件夹(现代浏览器)asyncfunctionreadDirectory(directory){constfiles=[];forawait(constentryofdirectory.values()){if(entry.isDirectory){constsubFiles=awaitreadDirectory(entry);subFiles.forEach(f=>{f.relativePath=entry.name+'/'+f.relativePath;files.push(f);});}else{files.push({file:awaitentry.getFile(),relativePath:entry.name});}}returnfiles;}-- 文件结构存储表CREATETABLE`file_structure`(`id`varchar(64)NOTNULL,`file_name`varchar(255)NOTNULL,`file_path`varchar(1024)NOTNULLCOMMENT'完整路径如/root/folder/file.txt',`parent_id`varchar(64)DEFAULTNULLCOMMENT'父目录ID',`is_directory`tinyint(1)NOTNULLDEFAULT'0',PRIMARYKEY(`id`),KEY`idx_parent`(`parent_id`))ENGINE=InnoDBDEFAULTCHARSET=utf8mb4;// 断点续传管理器classResumeUploader{constructor(){this.chunks={};}// 开始上传asyncupload(file){constfileId=this.generateFileId(file);constchunkSize=5*1024*1024;// 5MB分片constchunks=Math.ceil(file.size/chunkSize);// 从本地恢复进度constsavedProgress=localStorage.getItem(`upload_${fileId}`);if(savedProgress){this.chunks[fileId]=JSON.parse(savedProgress);}else{this.chunks[fileId]={uploaded:0,total:chunks};}// 上传剩余分片for(leti=this.chunks[fileId].uploaded;i<chunks;i++){constchunk=file.slice(i*chunkSize,(i+1)*chunkSize);awaitthis.uploadChunk(fileId,chunk,i);// 更新进度this.chunks[fileId].uploaded++;localStorage.setItem(`upload_${fileId}`,JSON.stringify(this.chunks[fileId]));}}}// 分片上传控制器@RestController@RequestMapping("/api/chunk")publicclassChunkUploadController{@PostMappingpublicStringuploadChunk(@RequestParamStringfileId,@RequestParamIntegerchunkNumber,@RequestParamMultipartFilechunk){// 1. 临时存储分片StringchunkPath="/tmp/uploads/"+fileId+"/"+chunkNumber;Files.write(Paths.get(chunkPath),chunk.getBytes());// 2. 检查是否所有分片都上传完成if(isUploadComplete(fileId)){mergeChunks(fileId);}return"success";}privatebooleanisUploadComplete(StringfileId){// 这里应该查询数据库或文件系统// 但预算只够返回truereturntrue;}}// 浏览器兼容层constFileUploader={// 现代浏览器上传modernUpload:async(files)=>{// 使用File API实现},// IE9专属上传ie9Upload:(files)=>{// 使用ActiveXObject实现try{constfso=newActiveXObject("Scripting.FileSystemObject");alert("检测到您在使用IE9,建议:\n1. 升级浏览器\n2. 加钱");returnthis.fakeUpload(files);}catch(e){alert("IE9都没装全?您这预算是不是该再加个0?");}},// 假装上传成功fakeUpload:(files)=>{return{success:true,message:"上传成功(可能丢失了部分文件)"};}};# 部署脚本:budget_deploy.shecho"正在部署价值100元的20G文件上传系统..."echo"1. 关闭所有安全组规则(省防火墙钱)"echo"2. 使用阿里云最便宜实例(共享型xn4)"echo"3. 数据库使用本地MySQL(省RDS钱)"echo"4. 关闭所有日志记录(省磁盘钱)"echo"部署完成!记得每天凌晨3点手动重启释放内存!"兄弟们,这个需求我最后是这么处理的:
最后报价单:
最终我决定:把甲方推荐给了群里做前端的张老三,自己拿20%介绍费美滋滋!
欢迎加入我们"夕阳红程序员接单群"(QQ:374992201),群里定期分享:
现在入群还能参与"最惨甲方需求"评选大赛,冠军将获得:
导入到Eclipse:点南查看教程
导入到IDEA:点击查看教程
springboot统一配置:点击查看教程
NOSQL示例不需要任何配置,可以直接访问测试
选择对应的数据表脚本,这里以SQL为例
up6/upload/年/月/日/guid/filename
支持离线保存文件进度,在关闭浏览器,刷新浏览器后进行不丢失,仍然能够继续上传
支持上传文件夹并保留层级结构,同样支持进度信息离线保存,刷新页面,关闭页面,重启系统不丢失上传进度。
点击下载完整示例