Spring Boot中如何设置MultipartFile请求参数为非必填
问题背景
- 基于Java Spring Boot开发
Portfolio实体编辑接口,接口同时接收实体属性与图片文件,设计逻辑为:参数为空时保留实体原有值,仅更新携带有效值的属性。 - 接口在Postman中测试运行正常,但从Angular表单提交
undefined/null值的图片属性时,后端抛出如下错误:
Resolved [org.springframework.web.multipart.support.MissingServletRequestPartException: Required request part 'file' is not present]
- 目标:实现
MultipartFile类型文件参数支持空值,提交空值时接口不报错。
原控制器代码如下:
@PutMapping("/portfolio/edit-data") public Portfolio editPortfolio (@RequestParam("image")MultipartFile image, @ModelAttribute("port") Portfolio port, BindingResult result){ Portfolio portOriginal = portServ.buscarPortfolio(Long.valueOf(1)); if(!image.isEmpty()){ String rutaAbsoluta = "C:\\Users\\Paul"; try{ byte[] bytesImg = image.getBytes(); Path fullPath = Paths.get(absolutePath + "//" + image.getOriginalFilename()); Files.write(fullPath, bytesImg); port.setImage(image.getOriginalFilename()); }catch (IOException e){ } }else{ port.setImage(portOriginal.getImagen()); } if("".equals(port.getName())){ port.setName(portOriginal.getName()); } if("".equals(port.getTitle())){ port.setTitle(portOriginal.getTitle()); } if("".equals(port.getIntroduction())){ port.setIntroduction(portOriginal.getIntroduction()); } if("".equals(port.getFooter())){ port.setFooter(portOriginal.getFooter()); } return portServ.editarPortfolio(port, Long.valueOf(1)); }
故障原因
@RequestParam注解默认配置为required = true,强制要求请求中必须携带对应参数/part。Angular提交时如果图片字段为undefined/null,构造FormData时会直接省略该part,后端接收不到对应part就会抛出MissingServletRequestPartException。Postman测试正常是因为测试空场景时通常会保留文件字段、仅上传空内容,和Angular省略字段的提交行为不一致。- 原代码存在两处逻辑缺陷:
- 定义了路径变量
rutaAbsoluta,写文件时误用了未定义的absolutePath变量,会触发运行错误 - 字符串属性仅判断了空字符串场景,未处理
null值,Angular提交undefined/null时后端接收到null,原有空判断会失效,无法正确保留原值
- 定义了路径变量
修复方案
后端代码调整
给文件参数的@RequestParam注解添加required = false声明非必填,同时修复原有代码缺陷:
@PutMapping("/portfolio/edit-data") public Portfolio editPortfolio (@RequestParam(value = "image", required = false) MultipartFile image, @ModelAttribute("port") Portfolio port, BindingResult result){ Portfolio portOriginal = portServ.buscarPortfolio(Long.valueOf(1)); String rutaAbsoluta = "C:\\Users\\Paul"; // 同时判断image不为null、内容不为空 if(image != null && !image.isEmpty()){ try{ byte[] bytesImg = image.getBytes(); Path fullPath = Paths.get(rutaAbsoluta + "//" + image.getOriginalFilename()); Files.write(fullPath, bytesImg); port.setImage(image.getOriginalFilename()); }catch (IOException e){ // 不要空吞异常,建议添加日志打印 e.printStackTrace(); } }else{ port.setImage(portOriginal.getImagen()); } // 补充null值判断 if(port.getName() == null || "".equals(port.getName())){ port.setName(portOriginal.getName()); } if(port.getTitle() == null || "".equals(port.getTitle())){ port.setTitle(portOriginal.getTitle()); } if(port.getIntroduction() == null || "".equals(port.getIntroduction())){ port.setIntroduction(portOriginal.getIntroduction()); } if(port.getFooter() == null || "".equals(port.getFooter())){ port.setFooter(portOriginal.getFooter()); } return portServ.editarPortfolio(port, Long.valueOf(1)); }
前端Angular适配(可选,建议同步配置)
构造FormData时即使文件为空也不要省略image字段,空值时传空字符串即可,保证前后端提交行为和Postman测试一致:
const formData = new FormData(); // 普通字段空值时传空字符串,避免后端接收到null formData.append('name', this.form.get('name')?.value || ''); formData.append('title', this.form.get('title')?.value || ''); formData.append('introduction', this.form.get('introduction')?.value || ''); formData.append('footer', this.form.get('footer')?.value || ''); // 文件字段存在则传文件,不存在则传空字符串,不要跳过append const imageFile = this.form.get('image')?.value; if (imageFile) { formData.append('image', imageFile, imageFile.name); } else { formData.append('image', ''); } // 发起PUT请求提交formData即可
内容的提问来源于stack exchange,提问作者Charly
相关产品推荐
相关产品推荐

