React Native Expo无法发送带图片的Multipart/Form POST请求(Spring后端)
React Native Expo + Spring 后端:multipart/form-data 请求丢失advertisement字段问题
问题描述
React Native Expo移动端应用搭配Spring后端,发送包含图片的multipart/form-data类型POST请求时,后端抛出Required part 'advertisement' is not present错误。基于React的Web应用使用几乎相同的代码可正常运行,但移动端处理multipart请求时异常。
前端核心代码(handlePost函数)
const handlePost = async () => { const formDataToSend = new FormData(); const advertisementBlob = new Blob([JSON.stringify(formData)], { type: "application/json", }); formDataToSend.append("advertisement", advertisementBlob); imageUris.forEach((img) => { const media = { uri: img, type: "image/jpeg", name: "profile.jpg", }; // const file = new File([blob]); formDataToSend.append("files", media); }); try { console.log(JSON.stringify(formDataToSend)); const result = await postAdd(formDataToSend); console.log(result); } catch (error) { console.log(error); } };
Spring后端报错日志
2024-07-14 02:10:54 2024-07-13T21:10:54.069Z WARN 1 --- [nio-3001-exec-8] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.multipart.support.MissingServletRequestPartException: Required part 'advertisement' is not present.]
后端核心代码
@PostMapping(value = "/create", consumes = "multipart/form-data") public ResponseEntity<?> saveAdvertisement(@RequestPart("advertisement") AdvertisementDTO aDto, @RequestPart("files") List<MultipartFile> files) { return facade.saveAdvertisement(aDto, files); } public ResponseEntity<?> saveAdvertisement(AdvertisementDTO aDto, List<MultipartFile> files) { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); User currentUser = (User) authentication.getPrincipal(); Advertisement advertisement = Advertisement.builder() .title(aDto.getTitle()) .description(aDto.getDescription()) .category(catService.findById(aDto.getCategory().getCategory_id())) .user(currentUser) .price(aDto.getPrice()) .date(LocalDateTime.now()) .views(0L) .build(); String result = iService.validateAndSaveImages(files); if (result != null) return ResponseEntity.badRequest().body(result); advertisement.setImages(files); try { service.save(advertisement); return ResponseEntity.ok().body("{\"status\":\"SUCCESS\"}"); } catch (Exception e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body("Advertisement creation failed: " + e.getMessage()); } }
请求配置
query: (newAdd) => { return { url: "/api/secured/create", method: "POST", body: newAdd, }; }, invalidatesTags: [{ type: "Advertisements", id: "LIST" }],
请求Payload JSON
{ "_parts": [ ["advertisement",{"_data":{"blobId":"923737fc-8e5d-43d9-adec-af0fb0ff933d","offset":0,"size":80,"type":"application/json","__collector":{}}}], ["files",{"uri":"file:///data/user/0/host.exp.exponent/cache/ExperienceData/%2540anonymous%252FCoreComponent-b964cf2e-b055-4d78-9e16-7ff1dbf5bec4/ImagePicker/83bd2379-6703-4a54-8764-e5427389de54.png","type":"image/jpeg","name":"profile.jpg"}] ] }
解决方案
1. 修正advertisement字段的FormData添加方式
React Native的FormData对Blob的处理逻辑和Web不同,直接传入Blob会导致后端无法正确解析该字段。改为直接传入JSON字符串,并指定文件名和类型:
// 替换原Blob相关代码 formDataToSend.append("advertisement", JSON.stringify(formData), { filename: "advertisement.json", type: "application/json" });
2. 禁止手动设置Content-Type请求头
确保请求工具(如fetch、react-query)没有手动设置Content-Type: multipart/form-data,让React Native自动生成包含boundary的请求头。手动设置会丢失boundary信息,导致后端无法正确拆分multipart数据。
3. 验证图片URI格式
如果使用Expo ImagePicker获取图片,确保返回的uri是file://开头的本地路径。若遇到Android路径问题,可使用expo-file-system验证文件存在:
import * as FileSystem from 'expo-file-system'; // 示例:验证文件存在 const verifyFile = async (uri) => { const info = await FileSystem.getInfoAsync(uri); return info.exists; };
内容的提问来源于stack exchange,提问作者Sayan Seksenbaev
相关产品推荐
相关产品推荐

