Spring Boot升级后之文件上传坑

HttpServlet源码分析 HttpServlet类是专门为HTTP协议准备的 HttpServlet在 jakarta.servlet.http.HttpServlet包下 目前接触的servlet规范中的接口: - jakarta.servlet.Servlet 核心接口(接口) - jakarta.servlet.ServletConfig Servlet配置信息接口(接口) - jakarta.servlet.ServletContext Servlet上下文接口(接口) -.. 阅读详情

Spring Boot升级后之文件上传坑

SpringBoot又双叒叕出新版本啦,不加班的人们都在用SpringBoot了,What?你还在用xml配置,赶紧拥抱SpringBoot,以后迎娶白富美不是梦,最近一次系统改造需要将18年的老系统改造成SpringBoot,优雅发版,减少加班,走上云时代,拥抱微服务。。。二大,赶紧把这个幻想症患者带走

咳咳咳。。。还是说正事,在改造的过程中遇到通过流文件上传的接口全部挂了,你没听错是全挂了

怎么回事呢,听我娓娓道来,业务代码不外泄,所以来个模拟

一、首先创建一个简单的包含WEB依赖的SpringBoot项目

pom.xml内容:

jetty容器

<!-- Spring Boot 版本 -->
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.3.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
  </parent>


<!-- Spring Boot web启动器 -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
      <exclusions>
        <exclusion>
          <artifactId>spring-boot-starter-tomcat</artifactId>
          <groupId>org.springframework.boot</groupId>
        </exclusion>
      </exclusions>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-jetty</artifactId>
    </dependency>

tomcat容器:

<!-- Spring Boot 版本 -->
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.1.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
  </parent>


<!-- Spring Boot web启动器 -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

二、配置文件上传的文件大小限制

application.properties配置文件添加:

# 上传文件总的最大值
spring.servlet.multipart.max-request-size=10MB
# 单个文件的最大值
spring.servlet.multipart.max-file-size=10MB


三、单文件上传示例

1、度娘一波,从网上抄了一段代码,项目中肯定是抄的这段,transferTo用的风生水起

package com.foo.bar.controller;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.StrUtil;
import java.io.File;
import java.io.IOException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

@Slf4j
@RestController
public class UploadController {

  @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
  public Dict local(@RequestParam("file") MultipartFile file) {
    if (file.isEmpty()) {
      return Dict.create().set("code", 400).set("message", "文件内容为空");
    }
    String fileName = file.getOriginalFilename();
    String rawFileName = StrUtil.subBefore(fileName, ".", true);
    String fileType = StrUtil.subAfter(fileName, ".", true);
    final String filePath = "/Users/foobar/foo/bar/";
    String localFilePath = filePath + rawFileName + "-" + DateUtil.current(false) + "." + fileType;
    }
    try {
      file.transferTo(new File(localFilePath));
    } catch (IOException e) {
      log.error("【文件上传至本地】失败,绝对路径:{}", localFilePath, e);
      return Dict.create().set("code", 500).set("message", "文件上传失败");
    }

    log.info("【文件上传至本地】绝对路径:{}", localFilePath);
    return Dict.create().set("code", 200).set("message", "上传成功").set("data", Dict.create().set("fileName", fileName).set("filePath", localFilePath));
  }

}

点了一下运行,准备一杯咖啡,喝完就下班,这时你眉头一紧,看到了亮眼的文件上传失败

然后点开了可爱的idea

/private/var/folders/7c/c1m8c2h52mx_pz716jlmmsd80000gn/T/foo/bar/001-1597844615672.jpg (No such file or directory)

[21:43:40.705] ERROR com.foo.bar.controller.UploadController 38 local - 【文件上传至本地】失败,绝对路径:/foo/bar/001-1597844615672.jpg java.io.FileNotFoundException: /private/var/folders/7c/c1m8c2h52mx_pz716jlmmsd80000gn/T/foo/bar/001-1597844615672.jpg (No such file or directory)
	at java.io.FileOutputStream.open0(FileOutputStream.java) ~[?:1.8.0_211]
	at java.io.FileOutputStream.open(FileOutputStream.java:270) ~[?:1.8.0_211]
	at java.io.FileOutputStream.<init>(FileOutputStream.java:213) ~[?:1.8.0_211]
	at java.io.FileOutputStream.<init>(FileOutputStream.java:162) ~[?:1.8.0_211]
	at org.eclipse.jetty.util.MultiPartInputStreamParser$MultiPart.write(MultiPartInputStreamParser.java:329) ~[jetty-util-9.4.29.v20200521.jar:9.4.29.v20200521]
	at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile.transferTo(StandardMultipartHttpServletRequest.java:256) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE]
	at com.foo.bar.controller.UploadController.local(UploadController.java:36) ~[classes/:?]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_211]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_211]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_211]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_211]
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE]
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE]
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:105) ~[spring-webmvc-5.2.7.RELEASE.jar:5.2.7.RELEASE]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:879) ~[spring-webmvc-5.2.7.RELEASE.jar:5.2.7.RELEASE]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793) ~[spring-webmvc-5.2.7.RELEASE.jar:5.2.7.RELEASE]
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.2.7.RELEASE.jar:5.2.7.RELEASE]
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040) ~[spring-webmvc-5.2.7.RELEASE.jar:5.2.7.RELEASE]
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943) ~[spring-webmvc-5.2.7.RELEASE.jar:5.2.7.RELEASE]
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.2.7.RELEASE.jar:5.2.7.RELEASE]
	at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909) ~[spring-webmvc-5.2.7.RELEASE.jar:5.2.7.RELEASE]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:523) ~[jakarta.servlet-api-4.0.3.jar:4.0.3]
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.2.7.RELEASE.jar:5.2.7.RELEASE]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:590) ~[jakarta.servlet-api-4.0.3.jar:4.0.3]
	at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:763) ~[jetty-servlet-9.4.29.v20200521.jar:9.4.29.v20200521]
	at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1631) ~[jetty-servlet-9.4.29.v20200521.jar:9.4.29.v20200521]
	at org.eclipse.jetty.websocket.server.WebSocketUpgradeFilter.doFilter(WebSocketUpgradeFilter.java:226) ~[websocket-server-9.4.29.v20200521.jar:9.4.29.v20200521]
	at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1618) ~[jetty-servlet-9.4.29.v20200521.jar:9.4.29.v20200521]
	at com.alibaba.druid.support.http.WebStatFilter.doFilter(WebStatFilter.java:124) ~[druid-1.1.23.jar:1.1.23]
	at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1618) ~[jetty-servlet-9.4.29.v20200521.jar:9.4.29.v20200521]
	at com.foo.bar.filter.LogFilter.doFilterInternal(LogFilter.java:97) ~[classes/:?]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE]
	at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1618) ~[jetty-servlet-9.4.29.v20200521.jar:9.4.29.v20200521]
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE]
	at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1618) ~[jetty-servlet-9.4.29.v20200521.jar:9.4.29.v20200521]
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE]
	at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1618) ~[jetty-servlet-9.4.29.v20200521.jar:9.4.29.v20200521]
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE]
	at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1618) ~[jetty-servlet-9.4.29.v20200521.jar:9.4.29.v20200521]
	at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:549) ~[jetty-servlet-9.4.29.v20200521.jar:9.4.29.v20200521]
	at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143) ~[jetty-server-9.4.29.v20200521.jar:9.4.29.v20200521]
	at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:602) ~[jetty-security-9.4.29.v20200521.jar:9.4.29.v20200521]
	at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127) ~[jetty-server-9.4.29.v20200521.jar:9.4.29.v20200521]
	at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:235) ~[jetty-server-9.4.29.v20200521.jar:9.4.29.v20200521]
	at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1610) ~[jetty-server-9.4.29.v20200521.jar:9.4.29.v20200521]
	at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:233) ~[jetty-server-9.4.29.v20200521.jar:9.4.29.v20200521]
	at org.eclipse.jetty.server.handler.ContextHandler.__doHandle(ContextHandler.java:1369) ~[jetty-server-9.4.29.v20200521.jar:9.4.29.v20200521]
	at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:42020) ~[jetty-server-9.4.29.v20200521.jar:9.4.29.v20200521]
	at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:188) ~[jetty-server-9.4.29.v20200521.jar:9.4.29.v20200521]
	at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:489) ~[jetty-servlet-9.4.29.v20200521.jar:9.4.29.v20200521]
	at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1580) ~[jetty-server-9.4.29.v20200521.jar:9.4.29.v20200521]
	at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:186) ~[jetty-server-9.4.29.v20200521.jar:9.4.29.v20200521]
	at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1284) ~[jetty-server-9.4.29.v20200521.jar:9.4.29.v20200521]
	at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141) ~[jetty-server-9.4.29.v20200521.jar:9.4.29.v20200521]
	at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127) ~[jetty-server-9.4.29.v20200521.jar:9.4.29.v20200521]
	at org.eclipse.jetty.server.Server.handle(Server.java:501) ~[jetty-server-9.4.29.v20200521.jar:9.4.29.v20200521]
	at org.eclipse.jetty.server.HttpChannel.lambda$handle$1(HttpChannel.java:383) ~[jetty-server-9.4.29.v20200521.jar:9.4.29.v20200521]
	at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:556) ~[jetty-server-9.4.29.v20200521.jar:9.4.29.v20200521]
	at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:375) ~[jetty-server-9.4.29.v20200521.jar:9.4.29.v20200521]
	at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:273) ~[jetty-server-9.4.29.v20200521.jar:9.4.29.v20200521]
	at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:311) ~[jetty-io-9.4.29.v20200521.jar:9.4.29.v20200521]
	at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:103) ~[jetty-io-9.4.29.v20200521.jar:9.4.29.v20200521]
	at org.eclipse.jetty.io.ChannelEndPoint$1.run(ChannelEndPoint.java:104) ~[jetty-io-9.4.29.v20200521.jar:9.4.29.v20200521]
	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:336) ~[jetty-util-9.4.29.v20200521.jar:9.4.29.v20200521]
	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:313) ~[jetty-util-9.4.29.v20200521.jar:9.4.29.v20200521]
	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:171) ~[jetty-util-9.4.29.v20200521.jar:9.4.29.v20200521]
	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.produce(EatWhatYouKill.java:135) ~[jetty-util-9.4.29.v20200521.jar:9.4.29.v20200521]
	at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:806) [jetty-util-9.4.29.v20200521.jar:9.4.29.v20200521]
	at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:938) [jetty-util-9.4.29.v20200521.jar:9.4.29.v20200521]
	at java.lang.Thread.run(Thread.java:748) [?:1.8.0_211]

[21:43:40.719] INFO  com.foo.bar.filter.LogFilter 107 doFilterInternal - 请求URL:/upload, total time:9480 ms, responseCode:200, requestBody:file=001.jpg(1470037 byte), responseBody:{"code":500,"message":"文件上传失败"}
2020-08-19 21:52:12 JRebel: Reloading class 'com.foo.bar.controller.UploadController'.

你一点也不慌,再一次打开了度娘

多年code经验告诉你这是路径问题,然后你找到了这个

解决使用Spring Boot、Multipartfile上传文件路径错误问题

目录不存在,我就自己建一个,没毛病,一行代码的事

final String filePath = "/foo/bar/";
String localFilePath = filePath + rawFileName + "-" + DateUtil.current(false) + "." + fileType;
final File dirPath = new File(filePath);
if(!dirPath.exists()){
  dirPath.mkdirs();
}

热部署一波,喝了一口咖啡,在postman上优雅地点了send

你惊讶的发现,报错信息一点没变,你开始怀疑热部署工具是不是坏了,暴躁的重启了server.又点了一下send.你开始惶恐了,依然是这个错误。喝了一口咖啡,回忆往昔,这个上传方法,你已用了无数次,为何这一次不行了呢,是不是因为springboot版本的问题,你再一次点开了度娘,运气真不错,又找到了一个解决方案。

springboot升级导致文件上传自动配置/tmp目录问题解决

接着你开启了debug大法,在这行代码中陷入了沉思,你不敢相信自己的眼睛,

MultiPartInputStreamParser已经被标记为过时了

 沉思并不能解决问题,加上了

spring.servlet.multipart.location: /foo/bar

再试一次。。。

很遗憾还是报错了,只是这次错误有点稍微不一样,没有赶紧又去掉了


[22:52:31.957] INFO  org.springframework.boot.StartupInfoLogger 61 logStarted - Started BarApplication in 4.417 seconds (JVM running for 8.069)
[22:54:24.754] ERROR com.foo.bar.controller.UploadController 35 local - 【文件上传至本地】失败,绝对路径:/foo/bar/001-1597848801337.jpg java.io.FileNotFoundException: /foo/bar/foo/bar/001-1597848801337.jpg (No such file or directory) 

你发现度娘找到解决方案屁用没有,内心一度崩溃。。。关上电脑,今夜不加班。架构改造完,业务代码凉凉,现在知道为啥不让你动老系统了吧,知道错了吧。

第二天醒来,灵光一闪,求人不如求己,开始了一边阅读源码,一边debug

一个

再一个

你似乎看到了一道光,为什么不用下面这个 重载的方法呢,度娘了一圈,你发现竟然没有人用,你似乎要做第一个吃螃蟹的人了

加上了这行载入史册的代码:

file.transferTo(Paths.get(localFilePath));

成功了!

package com.foo.bar.controller;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.StrUtil;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

@Slf4j
@RestController
public class UploadController {

  @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
  public Dict local(@RequestParam("file") MultipartFile file) {
    if (file.isEmpty()) {
      return Dict.create().set("code", 400).set("message", "文件内容为空");
    }
    String fileName = file.getOriginalFilename();
    String rawFileName = StrUtil.subBefore(fileName, ".", true);
    String fileType = StrUtil.subAfter(fileName, ".", true);
    final String filePath = "/Users/foobar/foo/bar/";
    String localFilePath = filePath + rawFileName + "-" + DateUtil.current(false) + "." + fileType;
    final File dirPath = new File(filePath);
    if(!dirPath.exists()){
      dirPath.mkdirs();
    }
    try {
      file.transferTo(Paths.get(localFilePath));
    } catch (IOException e) {
      log.error("【文件上传至本地】失败,绝对路径:{}", localFilePath, e);
      return Dict.create().set("code", 500).set("message", "文件上传失败");
    }

    log.info("【文件上传至本地】绝对路径:{}", localFilePath);
    return Dict.create().set("code", 200).set("message", "上传成功").set("data", Dict.create().set("fileName", fileName).set("filePath", localFilePath));
  }

}

可不可以不实用transferTo方法,度娘大法开启,点开了第一个

MultipartFile File相互转化

MultipartFile转File

File file = new File(path); 

FileUtils.copyInputStreamToFile(multipartFile.getInputStream(), file);  

这两个不就是一样的吗,So,还要用transferTo吗,这么坑比的方法,升级了Springboot版本就不能用了

		@Override
		public void transferTo(Path dest) throws IOException, IllegalStateException {
			FileCopyUtils.copy(this.part.getInputStream(), Files.newOutputStream(dest));
		}

彻底换掉这个坑比的transferTo,自己掌控流与文件,感觉真好

正确的文件上传方式:

1.流转文件

2.流转byte[]

package com.foo.bar.controller;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.StrUtil;
import java.io.File;
import java.io.IOException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

@Slf4j
@RestController
public class UploadController {

  @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
  public Dict local(@RequestParam("file") MultipartFile file) {
    if (file.isEmpty()) {
      return Dict.create().set("code", 400).set("message", "文件内容为空");
    }
    String fileName = file.getOriginalFilename();
    String rawFileName = StrUtil.subBefore(fileName, ".", true);
    String fileType = StrUtil.subAfter(fileName, ".", true);
    String destFilePath = "/Users/foobar/foo/bar/" + rawFileName + "-" + DateUtil.current(false) + "." + fileType;
    final File destFile = new File(destFilePath);
    if(!destFile.getParentFile().exists()){
      destFile.getParentFile().mkdirs();
    }
    try {
      FileUtils.copyInputStreamToFile(file.getInputStream(), destFile);
    } catch (IOException e) {
      log.error("【文件上传至本地】失败,绝对路径:{}", destFilePath, e);
      return Dict.create().set("code", 500).set("message", "文件上传失败");
    }

    log.info("【文件上传至本地】绝对路径:{}", destFilePath);
    return Dict.create().set("code", 200).set("message", "上传成功").set("data", Dict.create().set("fileName", fileName).set("filePath", destFilePath));
  }

}

友情忠告,没事别动架构,别升级,别升级

升级SpringBoot版本导致文件上传失败的问题 问题描述 因更换SpringBoot版本导致文件上传失败,版本如下: 老版本:spring-web:5.1.7.RELEASE 新版本:spring-web:5.2.4.RELEASE 其实新版本没有问题,只不过一些没有问题的代码合到一块就恰巧出现了问题 SpringMvc流程图 debug过程 我们都知道,SpringMVC是基于DispatcherServlet做统一请求处理的,那就从DispatcherServlet开始debug,下面是入口方法 protected void doService(H 阅读详情

相关推荐

Spring Controller原理与最佳实践:从MVC设计到Spring Boot 3.x落地

Spring Controller是Spring MVC架构中承接HTTP请求的核心组件,其本质是MVC模式在Java Web中的工程化实现,通过DispatcherServlet、HandlerMapping与HandlerAdapter协同完成请求路由、参数绑定与响应渲染。它并非简单注解标记,而是承担协议适配、数据契约定义与入口守门职责的关键层。理解@Controller与@RestController的语义差异、@RequestParam/@PathVariable/@RequestBody的协议层级

weixin_33901926的博客 133

(hutool)工具常用的时间类型

/获取系统当前日期并将数据转为date类型。1.获取时间格式为年与日时分秒格式的数据(yyyy-MM-dd HH:mm:ss)4.获取系统当前时间 格式:(yyyy-MM-dd HH:mm:ss)3.获取格式为(yyyy-MM-dd)2.将以上获取的时间转为date类型。6.计算时间相差多少天(天数)5.时间比较大小举例 (8.获取时间格式为(

qq_66768033的博客 2584

Spring Boot Base64文件上传:从解码到生产级安全存储全解析

Base64编码是一种将二进制数据转换为ASCII字符串的编码方案,常用于在文本协议(如JSON、XML)中安全传输文件数据。其原理是将每3个字节的二进制数据编码为4个可打印字符,便于网络传输且避免数据损坏。在Web开发中,这种技术价值在于能够无缝集成文件上传功能到基于JSON的RESTful API中,尤其适用于图片处理、Canvas绘图输出等前端直接生成Base64数据的场景。然而,直接将Base64字符串解码存储为文件,会面临内存溢出、恶意文件上传、存储扩展性等工程挑战。本文以Spring Boot框

weixin_34377919的博客 500

IOFileUploadException: Processing of multipart/form-data request failed. Stream ended unexpectedly

org.springframework.web.multipart.MultipartException: Failed to parse multipart servlet request; nested exception is java.io.IOException: org.apache.tomcat.util.http.fileupload.impl.IOFileUploadException: Processing of multipart/form-data request failed...

金戈铁马 5188

Spring Boot内置Tomcat版本修改实战:原理、方法与避坑指南

在Java Web开发中,Servlet容器是承载Web应用运行的核心组件,负责处理HTTP请求、管理会话和提供运行时环境。Spring Boot通过自动配置和Starter机制,默认集成了Tomcat作为嵌入式容器,实现了开箱即用的便捷性。其原理在于依赖管理,由spring-boot-dependencies统一控制版本号。这种设计在提升开发效率的同时,也带来了灵活性挑战,例如当需要修复安全漏洞、适配特定环境或利用新特性时,开发者必须掌握修改内置Tomcat版本的能力。通过覆盖Maven属性或调整Grad

weixin_30843605的博客 500

升级Spring Boot文件上传功能:从commons-fileupload到commons-fileupload2-jakarta

本文将探讨在Spring Boot应用程序中实现文件上传功能的必要性,以及为什么需要从过时的commons-fileupload库升级到现代的commons-fileupload2-jakarta库。我们将提供解决问题的方法和步骤,确保您的应用程序在性能、安全性和兼容性方面保持最佳状态。

专注于深入研究多种编程语言,以实战为导向,逐步拓展开发技能,提升工程化编码和思维能力,展现无敌技术实力。 2764

HttpServlet详解

HttpServlet、HttpServletResponse、HttpServletRequest详解

weixin_63127290的博客 1371

VoIP概述

VoIP统一通信与VoIPVoIP与传统电话比较VoIP各项产品设备的类型及应用 统一通信与VoIP 统一通信指的是把计算机与传统通信技术统一成一体的新通信模式,融合计算机网络与传统通信网络在一个网络平台上,实现电话、传真、数据传输、音视频会议、呼叫中心、即时通信等众多应用服务 VoIP又称IP电话或IP网络电话,是Voice Over IP的缩写,这种技术通过对语音信号进行编码数字化,压缩处理成压缩帧,然后转换成为IP数据包在TCP/IP网络上进行传输,从而达到了在IP网络上进行语音通信目的 VoIP与传

kaminao的博客 4357

spring boot 2 升级到 spring boot 3 后文件上传失败

以上就是升级 spring boot3 文件上传失败遇到的问题及解决方案。

随便写写 1929

ComfyUI-controlnet控制出图

提供加载预处理器节点。

Bu2n的博客 5494

SPSS线性混合模型实战:手把手复现一篇老年认知研究的统计结果(含代码)

本文详细介绍了如何使用SPSS线性混合效应模型复现老年认知研究的统计结果,涵盖数据准备、模型构建、结果解读及可视化全流程。特别针对重复测量数据分析,提供了SPSS菜单操作与语法编写的实用技巧,帮助研究者高效处理复杂数据并验证模型假设。

weixin_42562079的博客 516

升级 Spring Boot 3 配置讲解 —— 如何处理文件上传下载?

通过本文的介绍,你已经学会了如何在 Spring Boot 3 中处理文件上传和下载。Spring Boot 3 提供了更加简洁和强大的 API,使得文件操作变得更加容易。希望本文的内容能够帮助你在实际项目中更好地处理文件上传和下载的需求。如果你有任何问题或建议,欢迎在评论区留言讨论。

让 Java 再次伟大! 1113

基于FreeRTOS与MQTT的物联网技术应用系列——步进电机控制(三)SD卡驱动、FatFS等的移植和ini配置文件读取的实现

本文实现了基于STM32F107VC的金牛开发板的基于SPI模式的SD卡驱动和FatFS的移植,以及利用minini库对ini配置信息文件进行读取。

sqshining的专栏 2137

升级 Spring Boot 3 配置讲解 —— 支持断点传输的文件上传和下载功能

断点传输是指在文件传输过程中,如果传输中断(如网络故障或用户手动暂停),可以从断点处继续传输,而不需要重新开始。这种技术对于大文件传输尤为重要,因为它可以显著减少重复传输的时间和带宽消耗。断点上传:客户端可以将文件分成多个块(Chunk),分批次上传,服务器端根据上传的块信息进行合并。断点下载:客户端可以请求文件的某一部分,服务器端根据请求的范围返回对应的文件内容。通过本文的介绍,你已经学会了如何在 Spring Boot 3 中实现支持断点传输的文件上传和下载功能。

让 Java 再次伟大! 1275

STM32F103 实例应用(10)——I2C读写24C32

一、应用简介 本文介绍24C32的读写,基于野火的软件I2C程序。 board_i2c.h /**=========================================================================== @file board_i2c.h @brief 本文件是用于I2C驱动 @author 青梅煮久 @version r0.1 @date 2021/01/13 -------------------------.

qq_34118600的博客 3447

中职网络安全2021年国赛Wireshark流量分析题目解析

中职网络安全Wireshark流量分析题目解析

FRANXX_02的博客 6441

Spring文件上传避坑指南:为什么我推荐用MultipartFile而不是CommonsMultipartFile?

本文深入探讨了Spring文件上传中MultipartFile与CommonsMultipartFile的选择问题。通过对比设计哲学、配置复杂度、安全风险及版本演进,明确指出在现代Spring Boot项目中应优先使用MultipartFile接口,以实现代码解耦、简化配置并提升安全性,避免对特定实现的强依赖。

red88的博客 852

【Unity 实用工具篇】| Unity中 实现背景模糊效果,简单易用

背景模糊 是一个很实用的功能,它可以帮助游戏在视觉上有更好的专注力和表现力。 实现模糊的种类和方式有很多种,大家自行选择适合自己的就可以了。 下面一起来看下本文实现背景模糊的效果和使用方式吧!

努力前行,总会成为自己心中的那道光 4万+

Spring boot设置文件上传大小限制

Spring boot 设置文件上传大小限制

PorryLee 1万+

详解AXI4-Lite接口(3)--AXI4-Lite接口IP源码仿真及分析(Master接口)

通过本文您会学到:1、了解AXI4-Lite_Master接口的逻辑实现;2、加深Xilinx IP、仿真工具的使用;3、提高阅读时序图的能力。

孤独的单刀 6445

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

【springboot】万字操作指南 Spring Boot 2.x 升级到 3.x:微服务项目实战

摘要: 本文提供Spring Boot 2.x升级到3.x的实战指南,重点针对微服务项目。升级涉及JDK 17、Spring Framework 6、Jakarta EE等连锁变化,需系统规划版本矩阵(如Spring Boot 3.5.x + Spring Cloud 2025.0.x)。建议升级路线:先统一JDK 17环境,再处理依赖迁移(javax→jakarta)、Spring Security 6适配、OAuth2授权服务器改造等关键步骤。

Cjh20pan的博客 969

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

Spring Boot之如何升级内嵌tomcat版本

文章目录Spring Boot之如何升级内嵌tomcat版本1. 背景2. 过程2.1 升级单模块项目的tomcat版本2.2 升级包含多个模块的项目3. 项目有打包子模块如何升级尾声 Spring Boot之如何升级内嵌tomcat版本 1. 背景 根据信息安全运营团队发布的Tomcat-AJP协议漏洞风险预警,Tomcat的AJP协议存在高危漏洞(默认8009端口)由于存在实现缺陷导致相关参数...

二营长 1万+

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

Spring Boot 项目中关于文件上传与访问的配置方案

这行代码的本质功能:声明:告诉 Spring Boot,“兄弟,除了你自己管的地方,D盘PIC文件夹也是咱们的地盘”。放行:当浏览器请求一个图片(比如 1.jpg)时,Spring Boot 会去 D:/PIC 里把这个文件找出来,通过网络发给浏览器。如果没有这行配置:文件虽然存到了 D 盘,但 Spring Boot 装作看不见,浏览器访问时会返回 404 Not Found。

2402_85428625的博客 780

记一次升级 springboot 2.1.6 升级到 2.3.4 版本导致的文件上传失败问题

因为项目发布在阿里云服务器上,阿里云安全中心会对项目进行扫描,最近检测到项目上用的 springboot 框架版本存在反射型文件下载漏洞,所以需要升级 springboot 版本 这一版本升级可真是差点要人老命 我这边原本用的 springboot 版本是 2.1.6,需要升级到 2.3.4 升级固然很好,springboot 也增加了部分新功能,优化了一些 bug 但是,升级也去掉了一部分之前存在的功能 问题现象 springboot 升级之后,原本用的好好的文件上传咋就不行了呢,真让人头痛 报错信息.

wxw1997a的博客 2626

Spring Boot 2.x项目从JDK 8升级至JDK 17实战指南

Java作为企业级应用开发的核心语言,其版本演进深刻影响着技术生态。从JDK 8到JDK 17,Java平台通过模块化系统、新垃圾收集器(如ZGC)和语言特性增强,实现了安全性、性能与开发体验的全面提升。这些底层改进为高并发微服务、云原生应用提供了更稳固的运行时基础。对于广泛使用的Spring Boot 2.x框架项目,升级至JDK 17不仅是获取官方长期支持、提升安全性的必要举措,更是迈向现代Java技术栈的关键一步。本文将聚焦于Spring Boot 2.3-2.7项目升级过程中的核心挑战,系统讲解如何

weixin_34211761的博客 471
下一篇: 一次springboot aop 不生效的奇葩分析之旅
路飞说AI
博客等级 码龄10年 214粉丝 24原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值