private static final Path BASE_PATH = Paths.get("./uploads");
	@PostMapping("/uploadForm")
    public String uploadForm(@RequestPart("files") MultipartFile[] files) throws IOException {
        for (MultipartFile file : files) {
            String fileName = UUID.randomUUID() + "-" + file.getOriginalFilename();
            final Path path = BASE_PATH.resolve(fileName);
            try (InputStream inputStream = file.getInputStream()) {
                Files.write(path, inputStream.readAllBytes());
        // 处理上传的二进制数据
        return "success";
    String url = "http://localhost:8080/upload";
    final File file = new File("D:\\test\\2023032201-205680e2622740b5bb888b7e4801ebf0.mp4");
    RestTemplate restTemplate = new RestTemplate();
    // 设置请求头和请求体
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.MULTIPART_FORM_DATA);
    final Resource resource = new FileSystemResource(file);
    final MultiValueMap<String, Object> valueMap = new LinkedMultiValueMap<>();
    valueMap.add("files", resource);
    HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(valueMap, headers);
    ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, requestEntity, String.class);
    System.out.println(responseEntity.getStatusCode());

1.2 Spring WebFlux

  • multipart大小限制
spring:
  codec:
    # DataBuffer方式
    max-in-memory-size: 512MB
  webflux:
    multipart:
      max-in-memory-size: 512MB
      # 默认非流式
      streaming: false
  • controller
    private static final Path BASE_PATH = Paths.get("./uploads");
    @PostMapping("/upload")
    public Mono<List<String>> upload(@RequestPart("files") Flux<FilePart> files) {
        return files.flatMap(filePart -> {
            String fileName = UUID.randomUUID() + "-" + filePart.filename();
            final Path path = BASE_PATH.resolve(fileName);
            final File dest = path.toFile();
            return filePart.transferTo(dest).thenReturn(dest.getName());
        }).collectList();
    private static final Path BASE_PATH = Paths.get("./uploads");
	// 不推荐此种方式(文件名丢失)
    @PostMapping("/upload")
    public Mono<List<String>> upload(@RequestPart("files") Flux<DataBuffer> files) {
        return files.flatMap(dataBuffer -> {
            String fileName = UUID.randomUUID() + ".dat";
            final Path path = BASE_PATH.resolve(fileName);
            return DataBufferUtils.write(Mono.just(dataBuffer), path, StandardOpenOption.CREATE)
                    .then(Mono.just(fileName))
                    .onErrorResume(ex -> {
                        log.error("上传失败", ex);
                        return Mono.just("fail: " + ex.getMessage());
                    });
        }).collectList();
    String url = "http://localhost:8080/upload";
    final File file = new File("D:\\test\\2023032201-205680e2622740b5bb888b7e4801ebf0.mp4");
    final MediaType contentType = MediaType.MULTIPART_FORM_DATA;
    WebClient webClient = WebClient.builder().build();       
    webClient.post()
        .uri(url)
        .contentType(contentType)
        .body(BodyInserters.fromMultipartData("files", new FileSystemResource(file)))
        .exchangeToMono(response -> response.bodyToMono(String.class))
        .subscribe(System.out::println);
 	// 无限期等待结果(线上不允许使用)
    Thread.currentThread().join();
 

值得一说的是FilePartFormFieldPart均继承自Part,两者是平级关系。需要说明的是,客户端传输的org.springframework.core.io.Resource file part 对应的并不一定是 FilePart类型,也有可能是FormFieldPart,两者最主要的区别是,FilePart可以取到原始的文件名,FormFieldPart无法取得原始的文件名,这也是合理的,因为Resource有个派生类ByteArrayResource是基于内存的不存在文件名。

@see org.springframework.http.client.MultipartBodyBuilder

2. 二进制流

文件名参数的上传可以以post请求的url参数(注意)的形式进行上传,也可以在请求头header上传输。

URL参数编解码:

jsencodeURI(str)

javaURLEncoder.encode(str, "UTF-8")URLDecoder.decode(encodedStr, "UTF-8")

2.1 Spring MVC

	private static final Path BASE_PATH = Paths.get("./uploads");
	@PostMapping("/upload")
    public String upload(@RequestBody Resource resource) throws IOException {
        // 此处Resource类型一定是ByteArrayResource,哪怕客户端传输的是FileSystemResource
        String fileName = UUID.randomUUID() + ".dat";
        final Path path = BASE_PATH.resolve(fileName);
        try (InputStream inputStream = resource.getInputStream()) {
            Files.write(path, inputStream.readAllBytes());
        // 处理上传的二进制数据
        return "success";
	private static final Path BASE_PATH = Paths.get("./uploads");
	@PostMapping("/upload")
    public String upload(@RequestBody byte[] bytes) throws IOException {
        String fileName = UUID.randomUUID() + ".dat";
        final Path path = BASE_PATH.resolve(fileName);
        Files.write(path, bytes);
        // 处理上传的二进制数据
        return "success";
 

使用HttpHeaders.CONTENT_DISPOSITION请求头传输文件名

@PostMapping("/upload")
    public String upload(HttpServletRequest request, @RequestBody Resource resource) throws IOException {
        String originalFilename = extractFileName(request);
        String fileName;
        if (Objects.isNull(originalFilename)) {
            fileName =  UUID.randomUUID() + ".dat";
        } else {
            fileName =  UUID.randomUUID() + "-" + originalFilename;
        final Path path = BASE_PATH.resolve(fileName);
        try (InputStream inputStream = resource.getInputStream()) {
            Files.write(path, inputStream.readAllBytes());
        // 处理上传的二进制数据
        return "success";
     * 从请求头提取文件名
     * @param request 请求对象
     * @return 文件名
    private String extractFileName(HttpServletRequest request) {
        final String disposition = request.getHeader(HttpHeaders.CONTENT_DISPOSITION);
        if (Objects.nonNull(disposition)) {
            Pattern pattern = Pattern.compile("filename=\"(.+)\"");
            Matcher matcher = pattern.matcher(disposition);
            if(matcher.find()) {
                return matcher.group(1);
        return null;
    public static void main(String[] args) throws IOException {
        String url = "http://localhost:8080/upload";
        final File file = new File("D:\\test\\2023032201-205680e2622740b5bb888b7e4801ebf0.mp4");
        RestTemplate restTemplate = new RestTemplate();
        // 设置请求头和请求体
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        headers.set(HttpHeaders.CONTENT_DISPOSITION, String.format("attachment; filename=\"%s\"", file.getName()));
        try (FileInputStream inputStream = new FileInputStream(file)) {
            HttpEntity<ByteArrayResource> requestEntity = new HttpEntity<>(new ByteArrayResource(inputStream.readAllBytes()), headers);
            ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, requestEntity, String.class);
            System.out.println(responseEntity.getStatusCode());
 

值得一说的是MultipartFileResource均继承自InputStreamSource,两者是平级关系,MultipartFile用于接受表单文件参数,而Resource可用于接受二进制文件流。

2.2 Spring WebFlux

    @PostMapping("/upload")
    public Mono<String> upload(@RequestBody Mono<Resource> resourceMono) {
        return resourceMono.flatMap(resource -> {
            String fileName = UUID.randomUUID() + ".dat";
            final Path path = BASE_PATH.resolve(fileName);
            try (InputStream inputStream = resource.getInputStream()) {
                Files.write(path, inputStream.readAllBytes());
            } catch (IOException e) {
                log.error("上传失败", e);
                return Mono.error(e);
            return Mono.just(fileName);
        });
    @PostMapping("/upload")
    public Mono<String> upload(@RequestBody Mono<byte[]> bytesMono) {
        return bytesMono.flatMap(bytes -> {
            String fileName = UUID.randomUUID() + ".dat";
            final Path path = BASE_PATH.resolve(fileName);
            try {
                Files.write(path, bytes);
            } catch (IOException e) {
                log.error("上传失败", e);
                return Mono.error(e);
            return Mono.just(fileName);
        });
    String url = "http://localhost:8080/upload";
    final File file = new File("D:\\test\\2023032201-205680e2622740b5bb888b7e4801ebf0.mp4");
    final MediaType contentType = MediaType.APPLICATION_OCTET_STREAM;
    WebClient webClient = WebClient.builder().build();
    try (final FileInputStream inputStream = new FileInputStream(file)) {
        final InputStreamResource resource = new InputStreamResource(inputStream);
        final String encode = URLEncoder.encode(file.getName(), Charset.defaultCharset());
        webClient.post()
            .uri(url)
            .contentType(contentType)
            .header(HttpHeaders.CONTENT_DISPOSITION, String.format("attachment; filename=\"%s\"", encode))
            .body(BodyInserters.fromResource(resource))
            .exchangeToMono(response -> response.bodyToMono(String.class))
            .subscribe(System.out::println);
        // 无限期等待结果(线上不允许使用)
        Thread.currentThread().join();
 

使用HttpHeaders.CONTENT_DISPOSITION请求头传输文件名

    @PostMapping("/upload")
    public Mono<String> upload(@RequestBody Mono<byte[]> bytesMono, ServerHttpRequest request) {
        return bytesMono.flatMap(bytes -> {
            String originalFilename = extractFileName(request);
            String fileName;
            if (Objects.isNull(originalFilename)) {
                fileName =  UUID.randomUUID() + ".dat";
            } else {
                final String decode = URLDecoder.decode(originalFilename, Charset.defaultCharset());
                fileName =  UUID.randomUUID() + "-" + decode;
            final Path path = BASE_PATH.resolve(fileName);
            try {
                Files.write(path, bytes);
            } catch (IOException e) {
                log.error("上传失败", e);
                return Mono.error(e);
            return Mono.just(fileName);
        });
     * 从请求头提取文件名
     * @param request 请求对象
     * @return 文件名
    private String extractFileName(ServerHttpRequest request) {
        final String disposition = request.getHeaders().getFirst(HttpHeaders.CONTENT_DISPOSITION);
        if (Objects.nonNull(disposition)) {
            Pattern pattern = Pattern.compile("filename=\"(.+)\"");
            Matcher matcher = pattern.matcher(disposition);
            if(matcher.find()) {
                return matcher.group(1);
        return null;
                    WebFlux 上传文件1. 表单上传方式1.1 Spring MVC1.2 Spring WebFlux2. 二进制流2.1 Spring MVC2.2 Spring WebFlux
./gradlew build
使用测试数据运行数据库服务器
已创建一个Dockerfile来运行带有测试数据的数据库服务器。 请参阅src/test/resources/db的README.md文件
运行服务器
要运行该应用程序: 
./gradlew bootRun
				
本文选自孙卫琴的《精通Spring:Java Web开发技术详解》清华大学出版社出版 技术支持网址为:​​​www.javathinker.net/spring.jsp​​ ​本书对应的直播和录播课:​​​www.javathinker.net/zhibo.jsp​​ 孙卫琴的QQ学习答疑群:915851077 文件 @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public Mono&lt;String&gt; requestBodyFlux(@RequestPart("file") FilePart filePart) throws IOE...
maxparts: 1 maxFileSize: 5242880 #5M转换成bytes 5 * 1024 * 1024 #授权多数据源修改不了 用默认的redis 系统中采用redis2集群 package com.fcb.car.biz.config; import com.fasterxml.jackson.databind.ObjectMapper;
本文主要讲述webflux文件 在工作中我们经常遇到需要上文件至服务器,SpringMVC文件网上已有很多案例,但照搬到Webflux则行不通。原因在于在webflux中没有MultipartFile接口的实例,webflux是用FilePart这个接口文件的。 我的应用场景是将图片上至阿里云的OSS,OSSSDK提供了一个将InputStream上的接口。而要拿到InputSt...
@RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class TestMerchantRequire { protected WebTestClient client; @Autowired
本文只是简单使用SpringBoot2使用WebFlux的函数式编程简单使用,后续会继续写关于Webflux相关的文章。 最近一直在研究WebFlux,后续会陆续出一些相关的文章。 首先看一下Srping官网上的一张图,对比一下SpringMvcSpring WebFlux,如图: 在查看一下WebFlux的官方文档:docs.spring.io/spring/docs…,WebFlux提...
SpringMVC(注解)上文件需要注意的几个地方: 1、form的enctype=”multipart/form-data”,这个是上文件必须的 2、applicationContext.xml配置: 代码如下: <!– SpringMVC文件时,需要配置MultipartResolver处理器 –> <bean id=”multipartResolver” class=”org.springframework.web.multipart.commons.CommonsMultipartResolver”>     <property name=”defaultEncoding
SpringRaft是一个学位论文项目,包含在Servlet和Reactive堆栈中的Raft共识算法实现中。 此实现应该是模块化的,以便可以对其进行扩展,并且应该是通用的,以便可以在不同的用例中重用它们。 该项目的最终目的是在这种情况下比较两个堆栈。 在Raft实现之前和之后构建的示例和测试位于test-examples/ 。 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> 文件
三、WebFlux、下载、展示 一、文件    一个真实案例中,文件到服务器后,要继续上到 OSS,经实测 transferTo 的方案更完美。DataBufferUtils 的方案,到服务器没问题,但继续上到 OSS 后,图片总是只有上半身。 * 文件 @ApiOperation("轮播图 - 上") @PostMapping("/admin/chart/upload", consumes = [MediaType.MULTIPART_FORM_DATA_
SpringCloud Gateway是Spring全家桶中一个比较新的项目,Spring社区是这么介绍它的: 该项目借助Spring WebFlux的能力,打造了一个API网关。旨在提供一种简单而有效的方法来作为API服务的路由,并为它们提供各种增强功能,例如:安全性,监控和可伸缩性。 而在真实的业务领域,我们经常用SpringCloud Gateway来做微服务网关,如果你不理解微服务网关和统网关的区别,可以阅读此篇文章 Service Mesh和API Gateway关系深度 <multipart-config> <max-file-size>10485760</max-file-size> <max-request-size>10485760</max-request-size> <file-size-threshold>0</file-size-threshold> </multipart-config> 2. 在Spring MVC配置文件中添加以下配置,用于限制上文件的类型: <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!-- 限制上文件的类型 --> <property name="allowedFileTypes"> <value>image/jpeg</value> <value>image/png</value> <value>image/gif</value> </list> </property> </bean> 其中,allowedFileTypes属性用于指定允许上文件类型,可以根据实际需求进行配置。 以上就是在Spring MVC中配置上文件限制的步骤。