Serve Static Resources with Spring – 用Spring服务静态资源

最后修改: 2014年 11月 9日

1. Overview

1.概述

This tutorial will explore how to serve static resources with Spring using both XML and Java configuration.

本教程将探讨如何使用XML和Java配置,用Spring服务静态资源

2. Using Spring Boot

2.使用Spring Boot

Spring Boot comes with a pre-configured implementation of ResourceHttpRequestHandler to facilitate serving static resources.

Spring Boot 预先配置了 ResourceHttpRequestHandler 的实现,以便为静态资源提供服务。

By default, this handler serves static content from any of the /static, /public, /resources, and /META-INF/resources directories that are on the classpath. Since src/main/resources is typically on the classpath by default, we can place any of these directories there.

默认情况下,该处理程序从classpath上的任何/static、/public、/resources和/META-INF/resources目录中提供静态内容。由于src/main/resources通常默认在classpath上,我们可以将这些目录中的任何一个放在那里。

For example, if we put an about.html file inside the /static directory in our classpath, then we can access that file via http://localhost:8080/about.html. Similarly, we can achieve the same result by adding that file in the other mentioned directories.

例如,如果我们把一个about.html文件放在classpath中的/static目录内,那么我们可以通过http://localhost:8080/about.html访问该文件。同样地,我们可以通过在其他提到的目录中添加该文件来实现同样的结果。

2.1. Custom Path Patterns

2.1.自定义路径模式

By default, Spring Boot serves all static content under the root part of the request, /**Even though it seems to be a good default configuration, we can change it via the spring.mvc.static-path-pattern configuration property. 

默认情况下,Spring Boot在请求的根部下提供所有静态内容,/**尽管这似乎是一个很好的默认配置,我们可以通过spring.mvc.static-path-pattern 配置属性来改变它。

For example, if we want to access the same file via http://localhost:8080/content/about.html, we can say so in our application.properties:

例如,如果我们想通过http://localhost:8080/content/about.html 访问同一个文件,我们可以在我们的application.properties>中这样说:

spring.mvc.static-path-pattern=/content/**

In WebFlux environments, we should use the spring.webflux.static-path-pattern property.

在WebFlux环境中,我们应该使用spring.webflux.static-path-pattern属性。

2.2. Custom Directories

2.2.自定义目录

Similar to path patterns, it’s also possible to change the default resource locations via the spring.web.resources.static-locations configuration property. This property can accept multiple comma-separated resource locations:

与路径模式类似,也可以通过spring.web.resources.static-locations配置属性来改变默认的资源位置。该属性可以接受多个逗号分隔的资源位置。

spring.web.resources.static-locations=classpath:/files/,classpath:/static-files

Here we’re serving static contents from the /files and /static-files directories inside the classpath. Moreover, Spring Boot can serve static files from outside of the classpath:

在这里,我们从classpath中的/files/static-files目录中提供静态内容。此外,Spring Boot可以从classpath之外提供静态文件

spring.web.resources.static-locations=file:/opt/files

Here we’re using the file resource signature, file:/, to serve files from our local disk.

这里我们使用文件资源签名file:/,从我们的本地磁盘提供文件。

3. XML Configuration

3.XML配置

If we need to go the old fashion way with XML-based configuration, we can make good use of the mvc:resources element to point to the location of resources with a specific public URL pattern.

如果我们需要走基于XML的老路,我们可以很好地利用mvc:resources元素来指向具有特定公共URL模式的资源位置。

For example, the following line will serve all requests for resources coming in with a public URL pattern, like “/resources/**”, by searching in the “/resources/” directory under the root folder in our application:

例如,下面一行将通过在我们应用程序的根文件夹下的”/resources/“目录中搜索,为所有以公共URL模式进来的资源请求提供服务,如”/resources/**”,

<mvc:resources mapping="/resources/**" location="/resources/" />

Now we can access a CSS file like in the following HTML page:

现在我们可以像下面的HTML页面那样访问一个CSS文件。

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
    <link href="<c:url value="/resources/myCss.css" />" rel="stylesheet">
    <title>Home</title>
</head>
<body>
    <h1>Hello world!</h1>
</body>
</html>

4. The ResourceHttpRequestHandler

4、ResourceHttpRequestHandler

Spring 3.1. introduced the ResourceHandlerRegistry to configure ResourceHttpRequestHandlers for serving static resources from the classpath, the WAR, or the file system. We can configure the ResourceHandlerRegistry programmatically inside our web context configuration class.

Spring 3.1.引入了ResourceHandlerRegistry来配置ResourceHttpRequestHandlers 用于服务来自classpath、WAR或文件系统的静态资源。我们可以在我们的Web上下文配置类中以编程方式配置ResourceHandlerRegistry

4.1. Serving a Resource Stored in the WAR

4.1.服务于存储在WAR中的资源

To illustrate this, we’ll use the same URL as before to point to myCss.css, but now the actual file will be located in the WAR’s webapp/resources folder, which is where we should place static resources when deploying Spring 3.1+ applications:

为了说明这一点,我们将使用与之前相同的URL指向myCss.css,但现在实际文件将位于WAR的webapp/resources文件夹中,这是我们在部署Spring 3.1+应用程序时应该放置静态资源的地方。

@Configuration
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry
          .addResourceHandler("/resources/**")
          .addResourceLocations("/resources/");	
    }
}

Let’s analyze this example a bit. First, we configure the external-facing URI path by defining a resource handler. Then we map that external-facing URI path internally to the physical path where the resources are actually located.

让我们来分析一下这个例子。首先,我们通过定义一个资源处理器来配置面向外部的URI路径。然后,我们在内部将这个面向外部的URI路径映射到资源实际所在的物理路径。

We can, of course, define multiple resource handlers using this simple, yet flexible, API.

当然,我们可以使用这个简单而又灵活的API定义多个资源处理程序。

Now the following line in an html page would get us the myCss.css resource inside the webapp/resources directory:

现在,在html页面中的以下一行将获得myCss.css资源,在webapp/resources目录下。

<link href="<c:url value="/resources/myCss.css" />" rel="stylesheet">

4.2. Serving a Resource Stored in the File System

4.2.为存储在文件系统中的资源提供服务

Let’s say we want to serve a resource stored in the /opt/files/ directory whenever a request comes in for the public URL matching the /files/** pattern. We simply configure the URL pattern and map it to that particular location on the disk:

假设我们想为存储在/opt/files/目录下的资源提供服务,只要有一个与/files/**模式匹配的公共URL请求进来。我们只需配置URL模式并将其映射到磁盘上的那个特定位置。

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry
      .addResourceHandler("/files/**")
      .addResourceLocations("file:/opt/files/");
 }

For Windows users, the argument passed to addResourceLocations for this example would be “file:///C:/opt/files/“.

对于Windows用户,本例中传递给addResourceLocations的参数将是”file://C:/opt/files/“。

Once we configure the resource location, we can use the mapped URL pattern in our home.html to load an image stored in the file system:

一旦我们配置了资源位置,我们就可以在我们的home.html中使用映射的URL模式来加载存储在文件系统中的图像:

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
    <link href="<c:url value="/resources/myCss.css" />" rel="stylesheet">
    <title>Home</title>
</head>
<body>
    <h1>Hello world!</h1>
    <img alt="image"  src="<c:url value="files/myImage.png" />">
</body>
</html>

4.3. Configuring Multiple Locations for a Resource

4.3.为一个资源配置多个位置

What if we want to look for a resource in more than one location?

如果我们想在一个以上的地方寻找一个资源怎么办?

We can include multiple locations with the addResourceLocations method. The list of locations will be searched in order until the resource is found:

我们可以用addResourceLocations方法包括多个地点。地点列表将按顺序搜索,直到找到资源。

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry
      .addResourceHandler("/resources/**")
      .addResourceLocations("/resources/","classpath:/other-resources/");
}

The following curl request will display the Hello.html page stored in either the application’s webappp/resources or the other-resources folder in the classpath:

下面的curl请求将显示存储在应用程序的webappp/resources或classpath的other-resources文件夹中的Hello.html页面。

curl -i http://localhost:8080/handling-spring-static-resources/resources/Hello.html

5. The New ResourceResolvers

5.新的ResourceResolvers

Spring 4.1. provides, with the new ResourcesResolvers, different types of resource resolvers that can be used to optimize browser performance when loading static resources. These resolvers can be chained and cached in the browser to optimize request handling.

Spring 4.1.提供了新的ResourcesResolvers,不同类型的资源解析器,可用于优化加载静态资源时的浏览器性能。这些解析器可以在浏览器中被链化和缓存,以优化请求处理。

5.1. The PathResourceResolver

5.1.PathResourceResolver

This is the simplest resolver, and its purpose is to find a resource given a public URL pattern. In fact, if we don’t add a ResourceResolver to the ResourceChainRegistration, this is the default resolver.

这是最简单的解析器,它的目的是找到一个给定的公共 URL 模式的资源。事实上,如果我们不在ResourceResolver中添加ResourceChainRegistration,这就是默认的解析器。

Let’s look at an example:

我们来看看一个例子。

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry
      .addResourceHandler("/resources/**")
      .addResourceLocations("/resources/","/other-resources/")
      .setCachePeriod(3600)
      .resourceChain(true)
      .addResolver(new PathResourceResolver());
}

Things to notice:

需要注意的事情。

  • We’re registering the PathResourceResolver in the resource chain as the sole ResourceResolver in it. We can refer to section 4.3. to see how to chain more than one ResourceResolver.
  • The resources served will be cached in the browser for 3600 seconds.
  • The chain is finally configured with the method resourceChain(true).

Now for the HTML code that, in conjunction with the PathResourceResolver, locates the foo.js script in either the webapp/resources or the webapp/other-resources folder:

现在是HTML代码,它与PathResourceResolver一起,在webapp/resourceswebapp/other-resources文件夹中找到了foo.js脚本。

<script type="text/javascript" src="<c:url value="/resources/foo.js" />">

5.2. The EncodedResourceResolver

5.2.EncodedResourceResolver

This resolver attempts to find an encoded resource based on the Accept-Encoding request header value.

这个解析器试图根据Accept-Encoding请求头值找到一个编码的资源。

For example, we may need to optimize bandwidth by serving the compressed version of a static resource using gzip content coding.

例如,我们可能需要通过使用gzip内容编码提供静态资源的压缩版本来优化带宽。

To configure an EncodedResourceResolver, we need to configure it in the ResourceChain, just as we configured the PathResourceResolver:

要配置EncodedResourceResolver,我们需要在ResourceChain中配置它,正如我们配置PathResourceResolver一样。

registry
  .addResourceHandler("/other-files/**")
  .addResourceLocations("file:/Users/Me/")
  .setCachePeriod(3600)
  .resourceChain(true)
  .addResolver(new EncodedResourceResolver());

By default, the EncodedResourceResolver is configured to support br and gzip codings.

默认情况下,EncodedResourceResolver被配置为支持brgzip的编码。

So the following curl request will get the zipped version of the Home.html file located in the file system in the Users/Me/ directory:

所以下面的curl请求将得到位于文件系统中Users/Me/目录的Home.html文件的压缩版。

curl -H  "Accept-Encoding:gzip" 
  http://localhost:8080/handling-spring-static-resources/other-files/Hello.html

Notice how we’re setting the header’s “Accept-Encoding” value to gzip. This is important because this particular resolver will only kick in if the gzip content is valid for the response.

注意我们如何将头的”Accept-Encoding“值设置为gzip。这很重要,因为这个特殊的解析器只有在gzip内容对响应有效时才会启动。

Finally, note that, same as before, the compressed version will remain available for the period of time it is cached in the browser, which in this case is 3600 seconds.

最后,请注意,与之前一样,压缩版在浏览器中的缓存时间内仍然可用,在本例中是3600秒。

5.3. Chaining ResourceResolvers

5.3.串联ResourceResolvers

To optimize resource lookup, ResourceResolvers can delegate the handling of resources to other resolvers. The only resolver that can’t delegate to the chain is the PathResourceResolver, which we should add at the end of the chain.

为了优化资源查找,ResourceResolvers可以将资源的处理委托给其它解析器。唯一不能委托给链的解析器是PathResourceResolver,,我们应该在链的末端添加它。

In fact, if the resourceChain isn’t set to true, then by default only a PathResourceResolver will be used to serve resources. Here we’re chaining the PathResourceResolver to resolve the resource if the GzipResourceResolver is unsuccessful:

事实上,如果resourceChain没有设置为true,那么默认情况下只有PathResourceResolver会被用于服务资源。在这里,如果GzipResourceResolver不成功,我们就用PathResourceResolver链来解析资源。

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry
      .addResourceHandler("/js/**")
      .addResourceLocations("/js/")
      .setCachePeriod(3600)
      .resourceChain(true)
      .addResolver(new GzipResourceResolver())
      .addResolver(new PathResourceResolver());
}

Now that we’ve added the /js/** pattern to the ResourceHandler, let’s include the foo.js resource, located in the webapp/js/ directory in our home.html page:

现在我们已经将/js/**模式添加到ResourceHandler中,让我们将foo.js资源,位于webapp/js/目录下的home.html页中。

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
    <link href="<c:url value="/resources/bootstrap.css" />" rel="stylesheet" />
    <script type="text/javascript"  src="<c:url value="/js/foo.js" />"></script>
    <title>Home</title>
</head>
<body>
    <h1>This is Home!</h1>
    <img alt="bunny hop image"  src="<c:url value="files/myImage.png" />" />
    <input type = "button" value="Click to Test Js File" onclick = "testing();" />
</body>
</html>

It’s worth mentioning that, as of Spring Framework 5.1, the GzipResourceResolver has been deprecated in favor of the EncodedResourceResolver. Therefore, we should avoid using it in the future.

值得一提的是,从 Spring Framework 5.1 开始,GzipResourceResolver已经被弃用,而改为EncodedResourceResolver。因此,我们应该在未来避免使用它。

6. Additional Security Configuration

6.额外的安全配置

If using Spring Security, it’s important to allow access to static resources. We’ll need to add the corresponding permissions for accessing the resource URL:

如果使用Spring Security,允许访问静态资源是很重要的。我们需要为访问资源的URL添加相应的权限。

<intercept-url pattern="/files/**" access="permitAll" />
<intercept-url pattern="/other-files/**/" access="permitAll" />
<intercept-url pattern="/resources/**" access="permitAll" />
<intercept-url pattern="/js/**" access="permitAll" />

7. Conclusion

7.结论

In this article, we illustrated various ways in which a Spring application can serve static resources.

在这篇文章中,我们说明了Spring应用程序为静态资源服务的各种方式。

The XML-based resource configuration is a legacy option that we can use if we can’t go down the Java configuration route yet.

基于XML的资源配置是一个传统的选项,如果我们还不能走Java配置路线,我们可以使用它。

Spring 3.1. came out with a basic programmatic alternative through its ResourceHandlerRegistry object.

Spring 3.1.推出了通过其ResourceHandlerRegistry对象的基本程序化替代方案。

Finally, the new out of the box ResourceResolvers and ResourceChainRegistration object that shipped with Spring 4.1. offers resource loading optimization features, like caching and resource handler chaining, to improve efficiency in serving static resources.

最后,新的开箱即用的ResourceResolversResourceChainRegistration对象,随Spring 4.1出货。 提供资源加载优化功能,如缓存和资源处理程序链,以提高服务静态资源的效率。

As always, the full example is available over on Github. Additionally, Spring Boot related source codes are also available in this project.

一如既往,完整的示例可在 Github 上获取。此外,Spring Boot 的相关源代码也可在该项目中获得