spring mvc怎么获取上传文件的原路径

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring mvc怎么获取上传文件的原路径相关的知识,希望对你有一定的参考价值。

@RequestMapping(value = "/upload", method = RequestMethod.POST)
public ModelAndView onSubmit(HttpServletRequest request,
HttpServletResponse response) throws Exception

MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
SimpleDateFormat dateformat = new SimpleDateFormat("yyyy/MM/dd/HH");
/** 构建文件保存的目录* */
String logoPathDir = "/business/shops/upload/"
+ dateformat.format(new Date());
/** 得到文件保存目录的真实路径* */
String logoRealPathDir = request.getSession().getServletContext()
.getRealPath(logoPathDir);
/** 根据真实路径创建目录* */
File logoSaveFile = new File(logoRealPathDir);
if (!logoSaveFile.exists())
logoSaveFile.mkdirs();
/** 页面控件的文件流* */
MultipartFile multipartFile = multipartRequest.getFile("file");
/** 获取文件的后缀* */
String suffix = multipartFile.getOriginalFilename().substring(
multipartFile.getOriginalFilename().lastIndexOf("."));
/** 使用UUID生成文件名称* */
String logImageName = UUID.randomUUID().toString() + suffix;// 构建文件名称
/** 拼成完整的文件保存路径加文件* */
String fileName = logoRealPathDir + File.separator + logImageName;
File file = new File(fileName);
try
multipartFile.transferTo(file);
catch (IllegalStateException e)
e.printStackTrace();
catch (IOException e)
e.printStackTrace();

/** 打印出上传到服务器的文件的绝对路径* */
System.out.println("****************"+fileName+"**************");
insertDate(fileName);
return new ModelAndView("redirect:/business/shops/my.jsp");
参考技术A 将文件转换为CommonsMultipartFile类型然后file.getFileItem().getName();即可

Spring MVC学习(10)—文件上传配置DispatcherServlet的路径配置请求和响应内容编码

  基于最新Spring 5.x,详细介绍了Spring MVC项目的一些特殊配置,包含文件上传的配置,DispatcherServlet的路径配置,请求体和响应体的编码配置。

  本次我们来学习Spring MVC项目的一些特殊配置,包含文件上传的配置,DispatcherServlet的路径配置,请求体和响应体的编码配置。

Spring MVC学习 系列文章

Spring MVC学习(1)—MVC的介绍以及Spring MVC的入门案例

Spring MVC学习(2)—Spring MVC中容器的层次结构以及父子容器的概念

Spring MVC学习(3)—Spring MVC中的核心组件以及请求的执行流程

Spring MVC学习(4)—ViewSolvsolver视图解析器的详细介绍与使用案例

Spring MVC学习(5)—基于注解的Controller控制器的配置全解【一万字】

Spring MVC学习(6)—Spring数据类型转换机制全解【一万字】

Spring MVC学习(7)—Validation基于注解的声明式数据校验机制全解【一万字】

Spring MVC学习(8)—HandlerInterceptor处理器拦截器机制全解

Spring MVC学习(9)—项目统一异常处理机制详解与使用案例

Spring MVC学习(10)—文件上传配置、DispatcherServlet的路径配置、请求和响应内容编码

Spring MVC学习(11)—跨域的介绍以及使用CORS解决跨域问题

1 Spring MVC文件上传

  org.springframework.web.multipart包提供了一个用于处理文件上传的Multipart多部分解析框架,使用该框架可以非常方便的实现文件上传,避免了编写繁琐的底层API。该框架的核心类是一个解析包括文件上传在内的多部分请求的MultipartResolver策略接口,以及用于在web应用中访问Multipart多部分内容的HttpServletRequest的扩展接口MultipartHttpServletRequest
  要启用多部分处理,我们需要在DispatcherServlet关联的Spring 配置文件中声明一个MultipartResolver 的bean,并命名为“multipartResolver”。DispatcherServlet会检测到该bean,并将其应用到传入的请求分析过程中。
  当收到一个content-type为multipart/form-data的POST请求时,多部分解析器将分析请求内容并包装当前 HttpServletRequest为一个MultipartHttpServletRequest,通过MultipartHttpServletRequest可以对已解析部分的访问,以及可以将已解析部分直接作为请求处理器方法的参数。
  Spring 3.1开始,Spring MVC为我们提供了MultipartResolver的两种实现,一种是基于ApacheCommons FileUpload的实现——CommonsMultipartResolver,另一种是基于基于Servlet 3.0多部分请求解析的实现StandardServletMultipartResolver

1.1 Apache Commons FileUpload

  想要使用Apache Commons FileUpload,我们必须引入commons-fileupload的依赖。

<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-
fileupload -->
<dependency>
  <groupId>commons-fileupload</groupId>
  <artifactId>commons-fileupload</artifactId>
  <version>1.4</version>
</dependency>

  在Spring MVC配置文件中配置名为multipartResolverCommonsMultipartResolver实现,并且可以配置其中的属性:

<bean id="multipartResolver" 
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <!--设置允许上传的最大大小(以byte字节为单位)。-1 表示没有限制(默认值)。-->
    <property name="maxUploadSize" value="#{10*1024*1024}"/>
    <!--设置为用于分析请求、应用于单个部件的标头和窗体字段的将默认字符编码。根据 Servlet 规范,默认值为 ISO-8859-1-->
    <property name="defaultEncoding" value="UTF-8"/>
</bean>

1.2 Servlet 3.0

  Servlet 3.0开始默认支持multipart解析,因此我们不需要再引入外部依赖,但是需要支持Servlet3.0的容器,tomcat服务器自7.0.x版本开始就支持Servlet3.0了,详细支持信息:http://tomcat.apache.org/whichversion.html
  在确定服务器支持Servlet3.0之后,在Spring MVC配置文件中配置名为multipartResolverCommonsMultipartResolver实现:

<bean id="multipartResolver" 
class="org.springframework.web.multipart.support.StandardServletMultipartResolver"/>

  如果想要自定义上传配置,那么需要在DispatcherServlet的声明中配置<multipart-config>

<servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet
    </servlet-class>
    <!--Servlet WebApplicationContext子容器的配置-->
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring-mvc-config.xml</param-value>
    </init-param>
    <load-on-startup>0</load-on-startup>

    <!--文件上传配置-->
    <multipart-config>
        <!--上传的文件的最大限制,单位byte-->
        <max-file-size>4194304</max-file-size>
        <!--multipart/form-data请求的最大限制,单位byte-->
        <max-request-size>10485760</max-request-size>
        <!--将存储上载文件的目录位置-->
        <!--<location></location>-->
    </multipart-config>
</servlet>

1.3 上传文件

  无论是基于Apache Commons FileUpload还是Servlet3.0,上传文件的代码都差不多!前端应该发送multipart/form-data的POST请求,并且请求的内容可以作为常规请求参数进行解析和访问!
  一个简单的前端页面如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>File Upload</title>
</head>
<body>
<h3>Springmvc文件上传</h3>
<form action="/mvc/upload" method="post" enctype="multipart/form-data">
    name: <label><input type="text" name="name"/></label>
    file:<label><input type="file" name="file"/></label><br>
    <input type="submit" value="上传"/>
</form>
</body>
</html>

  后端Controller如下,其中MultipartFile用于接收上传的文件:

@RestController
public class MultipartController {
    @PostMapping("/upload")
    public void upload(String name, MultipartFile file) {
        System.out.println(name);
        System.out.println(file.getName());
        System.out.println(file.getOriginalFilename());
    }
}

  将参数类型声明为List<MultipartFile>表示允许为同一参数名称解析多个文件。
  将参数类型声明为Map<String, MultipartFile>或者MultiValueMap<String, MultipartFile>时,并且在@RequestParam注解中未指定参数名称的情况下,参数Map将填充映射每个给定参数名称以及对应的multipart文件。
  如果使用 Servlet 3.0 多部分解析,那么还可以声明 javax.servlet.http.part 而不是 Spring 的MultipartFile来接收上传的文件。
  当然multipart内容可以和命令对象(command object)绑定在一起,例如,下面示例中的表单字段和文件可以是一个对象中的字段,如下所示:

public class MultipartModel {
    private String name;
    private MultipartFile file;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public MultipartFile getFile() {
        return file;
    }

    public void setFile(MultipartFile file) {
        this.file = file;
    }
}

  Controller如下:

@RestController
public class MultipartController {
    @PostMapping("/upload")
    public void upload(MultipartModel multipartModel) {
        System.out.println(multipartModel.getName());
        System.out.println(multipartModel.getFile().getName());
        System.out.println(multipartModel.getFile().getSize());
    }
}

2 DispatcherServlet路径

  想要明白DispatcherServlet的路径配置,我们应该明白原始Servlet的路径规则并且对Spring MVC的组件有所了解,这些我们在此前就在文章《Java Web(4)—Servlet的概念以及Servlet开发案例》的Servlet的细节部分学习过了,这里不再赘述!

  1. DispatcherServlet的映射路径url-pattern如果设置为“/”,此时普通请求都会进入该Servlet,但是对于访问静态资源的请求同样会被转发到该Servlet中,并且请求的路径被默认认定为Handler映射路径,即普通请求,这将导致由于静态资源请求因找不到Handler而抛出404异常。但是对于以“.jsp”结尾的动态资源访问,不会进入DispatcherServlet,因为它被tomcat容器为我们配置的路径为“*.jsp”JspServlet处理了。
  2. DispatcherServlet的映射路径url-pattern如果设置为“/*”,那么所有访问都会进入DispatcherServlet,包括以“.jsp”结尾的动态资源访问,并且请求的路径被默认认定为Handler映射路径,即普通请求,这将导致由于jsp请求和静态资源请求因找不到Handler而抛出404异常。

  通常我们配置DispatcherServlet的url-pattern“/”,此时想要让DispatcherServlet处理静态资源的请求,那么有下面两个方法!

2.1 配置静态资源

  通过<mvc:resources/>标签配置静态资源的位置和请求映射,该标签有如下几个属性:

  1. location表示静态资源的位置,可以指定多个不同的位置,使用“,”分隔。默认定位到webapp目录下,即应用根目录,可以使用classpath:指定本地类路径和jar包路径。、
  2. mapping表示资源请求的路径,可以使用**表示匹配任何路径。这些相对路径用于查找基于location路径的资源。
  3. cache-period表示静态资源的缓存时间,以确保记尽量减少浏览器发出的HTTP请求。

   使用该配置后,静态资源的访问仍然会首先被DispatcherServlet拦截。
  每一个<mvc:resources/>标签都将配置一个SimpleUrlHandlerMapping,并且它们的Order为Integer. MAX_VALUE,也就是排在handlerMapping链的末尾,在其内部的urlmap集合中,key为mapping的值,value为ResourceHttpRequestHandler。对于静态资源的访问,对应的handler就是ResourceHttpRequestHandler,而在后续获取的HandlerAdpater也是HttpRequestHandlerAdapter,我们在此前MVC组件就介绍过了,该处理器适配器转换用于处理通过Spring MVC来访问的静态资源的请求
  和其他HandlerAdapter一样,HttpRequestHandlerAdapter的handle方法内部仅仅是对ResourceHttpRequestHandler的handleRequest方法进行调用,因此静态资源的访问,实际上是通过ResourceHttpRequestHandler处理器来进行处理的,并且支持通过classpath引入本地类路径和jar包路径下的静态资源。最后返回的ModelAndView为null,也就是不再需要进行后续的视图处理了,还是很简单的!

  ResourceHttpRequestHandler的handleRequest方法中:

  1. 会根据路径找到真实的静态资源并加载为Resource,找不到就返回404
  2. 找到之后,会判断请求是否使用了If-Modified-Since头,如果使用了,那么该头部值会跟Resource资源的last-modified作比较,如果相等或者Resource的lastModified更小,则返回304状态代码,表示资源未被修改,浏览器可以使用已缓存的静态资源!
  3. 如果 Resource的lastModified更大或者没有使用If-Modified-Since头,那么从目标资源Resource获取InputStream,其中的数据会被读取为byte[]数组并写入response的OutputStream(通过流的形式返回响应),也就是响应体,最终在浏览器被渲染。

  一种常见的XML配置如下:

<mvc:resources location="/css/" mapping="/css/**"/>
<mvc:resources location="/images/" mapping="/images/**"/>
<mvc:resources location="/js/" mapping="/js/**"/>

  对应的JavaConfig配置如下:

/**
 * @author lx
 */
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/css/**").addResourceLocations("/css/");
        registry.addResourceHandler("/images/**").addResourceLocations("/images/");
        registry.addResourceHandler("/js/**").addResourceLocations("/js/");
        /*也可以如下样式,但是它们有些许区别*/
        //registry.addResourceHandler("/css/**", "/images/**", "/js/**")
        //        .addResourceLocations("/css/", "/images/", "/js/");
    }
}

  如果某个<mvc:resources/>标签的location为“/A/B/”mapping为“/C/D/**”,如果某个请求路径为“/C/D/E/index.html”,那么在后台查找的对应的资源真实位置为webapp下的“/A/B/E/index.html”

2.2 配置默认Servlet

  此前在Java EE部分的《Java Web(4)—Servlet的概念以及Servlet开发案例》的Servlet的细节部分我们就讲过了,tomcat容器会为web项目配置一个默认Servlet,专门用于处理静态资源的请求,并且url-pattern为“/”。也就是说,如果DispatcherServlet的url-pattern为“/”,那么表示我们是将DispatcherServlet作为默认Servlet,这将覆盖容器为我们配置的默认Servlet,因此静态资源也会由DispatcherServlet来处理,而如果我们没有特殊处理的话,静态资源的请求会因为被作为普通请求而找不到handler而返回404,除非向上面那样配置静态资源处理器!
  实际上,上面所说的“覆盖”并不是直接将容器为我们创建的DefaultServlet抹除了,而是默认只使用我们自己配置的默认Servlet,但是容器为我们配置的DefaultServlet仍然还是存在的!也就是说,如果我们能够获取容器配置的默认Servlet,那么我们或许就有另一种方式来处理静态资源了!
  
在Spring MVC中,允许将DispatcherServlet映射到“/”从而覆盖了容器默认Servlet的映射,但同时仍允许并且有办法获取容器创建的默认Servlet来处理静态资源请求!**
  如果是JavaConfig方式,那么可以如下配置:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
}

  如果是XML方式,那么可以加入如下配置(如果使用<mvc:default-servlet-handler/>标签,那么还需要添加<mvc:annotation-driven/>标签。):

<mvc:default-servlet-handler/>

   开启这个功能之后,静态资源的访问仍然会首先被DispatcherServlet拦截。

  实际上,同样是配置一个SimpleUrlHandlerMapping的处理器映射器,只不过它的order值为Integer.MAX_VALUE,也就是说,在HandlerMapping链中它排在最末尾,在其内部的urlmap集合中,key默认为“/**”,value为DefaultServletHttpRequestHandler。也就是说,如果在这个HandlerMapping之前所有的HandlerMapping都无法解析给定的请求路径为handler时,这些请求都会被最后一个SimpleUrlHandlerMapping所处理!在后续获取的HandlerAdpater也是HttpRequestHandlerAdapter
  为什么这个SimpleUrlHandlerMapping要放在HandlerMapping链的末尾呢?因为其对应的DefaultServletHttpRequestHandler在执行时会将所有到达此HandlerMapping的请求都转发到web应用真正的默认Servlet中,这个Servlet把请求URL专门用来查找静态资源!因此如果排在最前面,那么动态资源请求仍然会被转发到这个容器默认Servlet而导致异常,而排在最后面,则表示此前的所有的HandlerMapping都无法处理这个请求,那么这个请求可能就是静态资源的请求!
  DefaultServletHttpRequestHandler的处理请求的方法:
在这里插入图片描述
  tomcat配置的默认的Servlet名为“default”。在DefaultServletHttpRequestHandler中,会尝试将请求直接forward转发给默认的Servlet去处理,是不是很简单?

3 内容编码

  对于请求体和响应体的编码,Spring MVC已经为我们提供了一个现成的Filter——CharacterEncodingFilter,使用时我们只需要配置到web.xml文件中即可!

<filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <!--设置编码-->
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
    <!--对于request和response是否强制使用指定的编码-->
    <init-param>
        <param-name>forceEncoding</param-name>
        <param-value>true</param-value>
    </init-param>

    <!--&lt;!&ndash;对于request是否强制使用指定的编码&ndash;&gt;-->
    <!--<init-param>-->
    <!--    <param-name>forceRequestEncoding</param-name>-->
    <!--    <param-value>true</param-value>-->
    <!--</init-param>-->
    <!--&lt;!&ndash;对于response是否强制使用指定的编码&ndash;&gt;-->
    <!--<init-param>-->
    <!--    <param-name>forceResponseEncoding</param-name>-->
    <!--    <param-value>true</param-value>-->
    <!--</init-param>-->
</filter>
<filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

  其中encoding,表示要使用的编码格式,forceEncoding表示是否对请求和响应强制使用指定的编码(同时设置forceRequestEncoding和forceResponseEncoding),默认false。forceRequestEncoding表示是否对请求强制使用指定的编码,默认false,forceResponseEncoding表示是否对响应强制使用指定的编码,默认false。

  CharacterEncodingFilter的处理过程如下:

在这里插入图片描述
  源码还是很简单的:

  1. 如果指定了encoding,那么可能会应用指定编码:
    1. 如果forceRequestEncoding为true或者此前没有默认的请求编码,那么设置请求编码为的encoding;
    2. 如果forceResponseEncoding为true,那么设置响应编码为encoding;
  2. 如果未指定encoding,此CharacterEncodingFilter什么也不做,即不会设置任何编码,进入下一个Filter!

相关文章:
  https://spring.io/
  Spring Framework 5.x 学习
  Spring Framework 5.x 源码

如有需要交流,或者文章有误,请直接留言。另外希望点赞、收藏、关注,我将不间断更新各种Java学习博客!

以上是关于spring mvc怎么获取上传文件的原路径的主要内容,如果未能解决你的问题,请参考以下文章

spring mvc 怎么大小上传文件控制?

Spring Mvc 怎么上传超过全局配置的文件?

Spring Boot 上传文件 获取项目根路径 物理地址 resttemplate上传文件

Spring MVC学习(10)—文件上传配置DispatcherServlet的路径配置请求和响应内容编码

java spring mvc 客户端上传文件到服务器端

如何在spring mvc控制器中获取getServletContext()