You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

上传MultipartFile至服务器时触发MessageBodyProviderNotFoundException

问题:SpringBoot文件上传客户端Jersey请求报错(MessageBodyWriter not found)

问题背景

基于Gradle构建的SpringBoot项目包含文件上传功能,后端接口经Postman测试可正常接收文件,但客户端调用时出现错误。已在build.gradle中添加Jersey的multipart依赖。

已添加的依赖

implementation 'org.glassfish.jersey.media:jersey-media-multipart:2.34'

错误堆栈

Caused by: org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException: MessageBodyWriter not found for media type=multipart/form-data, type=class org.springframework.mock.web.MockMultipartFile, genericType=class org.springframework.mock.web.MockMultipartFile.
at org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.aroundWriteTo(WriterInterceptorExecutor.java:224)
at org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:139)
at org.glassfish.jersey.message.internal.MessageBodyFactory.writeTo(MessageBodyFactory.java:1116)
at org.glassfish.jersey.client.ClientRequest.doWriteEntity(ClientRequest.java:461)
at org.glassfish.jersey.client.ClientRequest.writeEntity(ClientRequest.java:443)
at org.glassfish.jersey.client.internal.HttpUrlConnector._apply(HttpUrlConnector.java:369)
Caused by: org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException: MessageBodyWriter not found for media type=multipart/form-data, type=class org.springframework.mock.web.MockMultipartFile, genericType=class org.springframework.mock.web.MockMultipartFile.

at org.glassfish.jersey.client.internal.HttpUrlConnector.apply(HttpUrlConnector.java:267)
at org.glassfish.jersey.client.ClientRuntime.invoke(ClientRuntime.java:297)
at org.glassfish.jersey.client.JerseyInvocation.lambda$invoke$2(JerseyInvocation.java:687)
at org.glassfish.jersey.client.JerseyInvocation.call(JerseyInvocation.java:697)
at org.glassfish.jersey.client.JerseyInvocation.lambda$runInScope$3(JerseyInvocation.java:691)
at org.glassfish.jersey.internal.Errors.process(Errors.java:292)
at org.glassfish.jersey.internal.Errors.process(Errors.java:274)
at org.glassfish.jersey.internal.Errors.process(Errors.java:205)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:390)
at org.glassfish.jersey.client.JerseyInvocation.runInScope(JerseyInvocation.java:691)
at org.glassfish.jersey.client.JerseyInvocation.invoke(JerseyInvocation.java:686)
at org.glassfish.jersey.client.JerseyInvocation$Builder.method(JerseyInvocation.java:461)
at org.glassfish.jersey.client.JerseyInvocation$Builder.post(JerseyInvocation.java:357)
at client.utils.ServerUtils.filePostRequest(ServerUtils.java:273)
at client.utils.ServerUtils.saveFile(ServerUtils.java:278)
at client.scenes.MediaPlayerCtrl.save(MediaPlayerCtrl.java:121)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
... 57 more

客户端代码

public void saveFile(File file) throws IOException {
        MultipartFile file1=convert(file);
        filePostRequest("secure/" + store.accessStore().getUsername() + "/file/add",
                Entity.entity(file1, "multipart/form-data"), new GenericType<>(){});
    }
public static MultipartFile convert(File file) throws IOException {
        String filename = file.getName();
        FileInputStream inputStream = new FileInputStream(file);
        return new MockMultipartFile(filename, inputStream);
    }
private <T> void filePostRequest(String path, Entity send, GenericType<T> retType) {
        ClientBuilder.newClient(new ClientConfig())
                .target(getServer()).path(path)
                .request("multipart/form-data")
                .post(send, retType);
    }

服务端Controller代码

@SpringBootApplication
@RestController
@RequestMapping(value = {"/secure/{username}/{password}/file", "/secure/{username}/file"})
public class FileController {
    @Autowired
    private StorageService service;
    @PostMapping("/add")
    public ResponseEntity<?> uploadFile(@RequestParam("file") MultipartFile file) throws IOException {
        String uploadFile = service.uploadFile(file);
        return ResponseEntity.status(HttpStatus.OK)
                .body(uploadFile);
    }

补充问题

尝试添加测试类注册MultiPartFeature,但IDE提示错误:Cannot access jakarta.ws.rs.core.Configurable

package server.api;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.glassfish.jersey.test.JerseyTest;

public class FileControllerTest extends JerseyTest {
    @Override
    protected void configureClient(ClientConfig conf) {
        conf.register(MultiPartFeature.class);
    }
}

解决方案

1. 修正客户端请求构建方式

Jersey客户端无法直接序列化Spring的MockMultipartFile为multipart请求体,需改用Jersey原生的FormDataMultiPart构建请求:

public void saveFile(File file) throws IOException {
    // 用Jersey API构建multipart请求体
    FormDataMultiPart multiPart = new FormDataMultiPart();
    try (FileInputStream inputStream = new FileInputStream(file)) {
        // 注意参数名"file"要和服务端@RequestParam("file")完全一致
        multiPart.field("file", inputStream, MediaType.APPLICATION_OCTET_STREAM_TYPE, file.getName());
        
        filePostRequest("secure/" + store.accessStore().getUsername() + "/file/add",
                Entity.entity(multiPart, multiPart.getMediaType()), new GenericType<>(){});
    } finally {
        multiPart.close();
    }
}

private <T> void filePostRequest(String path, Entity send, GenericType<T> retType) {
    // 必须将MultiPartFeature注册到ClientConfig
    ClientConfig config = new ClientConfig();
    config.register(MultiPartFeature.class);
    
    ClientBuilder.newClient(config)
            .target(getServer()).path(path)
            .request(MediaType.APPLICATION_JSON_TYPE) // 此处填写服务端返回的媒体类型,而非请求类型
            .post(send, retType);
}

2. 移除不必要的类型转换

无需将File转换为Spring的MockMultipartFile,直接用Jersey API处理文件流即可,避免跨框架类型不兼容问题。

3. 排查依赖版本冲突

  • 确保jersey-media-multipart版本与项目中其他Jersey依赖版本一致,避免版本冲突。
  • SpringBoot项目建议使用starter管理Jersey依赖,简化版本控制:
implementation 'org.springframework.boot:spring-boot-starter-jersey'
implementation 'org.glassfish.jersey.media:jersey-media-multipart:2.34'

4. 解决测试类错误

测试类的错误源于Jersey版本API不兼容:

  • 项目使用的Jersey 2.x基于JEE API,需确保测试依赖为2.x版本:
testImplementation 'org.glassfish.jersey.test-framework.providers:jersey-test-framework-provider-grizzly2:2.34'
  • 确认测试类导入的ClientConfig为org.glassfish.jersey.client.ClientConfig,而非Jakarta EE版本的包。

内容的提问来源于stack exchange,提问作者Micu Bogdan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 14:27:20