文件上传下载、Excel 导入导出业务实战

发布时间:2026/7/29 21:47:16
文件上传下载、Excel 导入导出业务实战 文件上传下载、Excel 导入导出业务实战用户要上传头像、老板要导出报表、运营要批量导入会员——文件操作三件套是每个后端绕不开的基本功。一、文件上传基础1.1 MultipartFile 接口SpringBoot 用MultipartFile接口封装上传文件一个 HTTPmultipart/form-data请求到达后框架自动把文件部分封装为MultipartFile对象。RestControllerRequestMapping(/file)publicclassFileController{PostMapping(/upload)publicResultupload(RequestParam(file)MultipartFilefile){// 获取原始文件名StringoriginalFilenamefile.getOriginalFilename();// 获取文件大小字节longsizefile.getSize();// 获取文件类型StringcontentTypefile.getContentType();// 获取输入流InputStreaminputStreamfile.getInputStream();returnResult.ok(originalFilename 上传成功);}}1.2 上传大小限制配置SpringBoot 默认最大上传文件为 1MB超出直接报错。在application.yml中调整spring:servlet:multipart:max-file-size:50MB# 单个文件最大大小max-request-size:100MB# 整个请求最大大小多文件时二、单文件/多文件上传单文件上传用RequestParam接收多文件上传用数组或 ListPostMapping(/upload/multi)publicResultuploadMulti(RequestParam(files)MultipartFile[]files){for(MultipartFilefile:files){if(!file.isEmpty()){StringfileNameUUID.randomUUID()_file.getOriginalFilename();FiledestnewFile(D:/uploads/fileName);file.transferTo(dest);// 保存到本地磁盘}}returnResult.ok(上传成功);}文件名用 UUID 前缀防止重名覆盖transferTo()一行完成磁盘写入。三、文件下载下载本质就是读取文件流设置 HTTP 响应头后写回浏览器GetMapping(/download/{fileName})publicResponseEntityResourcedownload(PathVariableStringfileName){FilefilenewFile(D:/uploads/fileName);if(!file.exists())returnResponseEntity.notFound().build();ResourceresourcenewFileSystemResource(file);returnResponseEntity.ok().contentType(MediaType.APPLICATION_OCTET_STREAM).header(HttpHeaders.CONTENT_DISPOSITION,attachment; filename\URLEncoder.encode(fileName,UTF-8)\).body(resource);}Content-Disposition设为attachment告诉浏览器弹下载框否则浏览器会直接预览。中文文件名须URLEncoder.encode防乱码。四、Excel 导入导出EasyExcel 方案4.1 Apache POI vs EasyExcel对比维度Apache POIEasyExcel (阿里出品)内存占用全量加载到内存大文件直接 OOM逐行读取内存占用极小速度慢快号称 100M 文件 6 秒读完易用性代码量大注解式代码简洁推荐程度历史遗留维护新项目首选EasyExcel 的原理是边读边处理、用完就扔而不是 POI 那样把所有单元格数据撑爆内存。4.2 添加依赖dependencygroupIdcom.alibaba/groupIdartifactIdeasyexcel/artifactIdversion3.3.3/version/dependency4.3 导出 Excel注解模式定义实体类用ExcelProperty标注列DatapublicclassUserExcelVO{ExcelProperty(用户ID)privateLongid;ExcelProperty(用户名)privateStringusername;ExcelProperty(年龄)privateIntegerage;ExcelProperty(邮箱)privateStringemail;}导出只需一行GetMapping(/export)publicvoidexport(HttpServletResponseresponse)throwsIOException{ListUserExcelVOlistuserService.listForExport();response.setContentType(application/vnd.ms-excel);response.setCharacterEncoding(utf-8);response.setHeader(Content-Disposition,attachment; filenameURLEncoder.encode(用户列表.xlsx,UTF-8));EasyExcel.write(response.getOutputStream(),UserExcelVO.class).sheet(用户列表).doWrite(list);}4.4 导入 Excel监听器模式关键是不能让 EasyExcel 把全部数据加载完再处理要边读边入库publicclassUserExcelListenerextendsAnalysisEventListenerUserExcelVO{privateUserServiceuserService;publicUserExcelListener(UserServiceuserService){this.userServiceuserService;}Overridepublicvoidinvoke(UserExcelVOdata,AnalysisContextcontext){// 每读取一行就处理一行不是读完再处理userService.saveFromExcel(data);}OverridepublicvoiddoAfterAllAnalysed(AnalysisContextcontext){log.info(Excel 导入完成);}}Controller 层调用PostMapping(/import)publicResultimportExcel(RequestParam(file)MultipartFilefile)throwsIOException{EasyExcel.read(file.getInputStream(),UserExcelVO.class,newUserExcelListener(userService)).sheet().doRead();returnResult.ok(导入成功);}五、文件上传安全三道防线5.1 文件类型校验别信contentType客户端能伪造也别只信后缀名。用文件魔数Magic Number文件头几个字节验证privatestaticbooleanisImage(MultipartFilefile){try(InputStreamisfile.getInputStream()){byte[]headernewbyte[4];is.read(header);// JPEG: FF D8 FF, PNG: 89 50 4E 47, GIF: 47 49 46 38StringhexbytesToHex(header);returnhex.startsWith(FFD8FF)||hex.startsWith(89504E47);}catch(IOExceptione){returnfalse;}}5.2 路径穿越防护攻击者上传../../etc/passwd这类文件名如果直接拼接路径文件可能写入到系统敏感目录。解决方式取文件名时去掉路径信息只用纯文件名StringfileNameFilenameUtils.getName(file.getOriginalFilename());5.3 文件大小限制在application.yml设了max-file-size还不够要在 Controller 层也做校验防止绕过代理if(file.getSize()50*1024*1024){returnResult.fail(文件大小超过限制);}六、常见问题与解决问题原因解决大文件 OOMPOI 全量加载换 EasyExcel 监听器模式中文文件名乱码浏览器编码不一致URLEncoder.encode(filename, UTF-8)临时文件堆积文件未清理定时任务清理过期文件多文件上传 413 错误max-request-size太小调大该配置文件操作看似简单坑都在细节里。安全校验、编码处理、内存控制三者缺一不可。