如何在Spring Boot嵌入式Tomcat中提供外部静态HTML文件的服务?

8

我对Spring框架和Spring Boot很陌生。
我实现了一个非常简单的RESTful Spring Boot web应用程序。
你可以在另一个问题中查看几乎完整的源代码:Spring Boot:如何将JDBC数据源配置外部化?

该应用程序如何提供外部静态HTML、CSS和JS文件呢?
例如,目录结构可能如下所示:

MyApp\
   MyApp.jar (this is the Spring Boot app that services the static files below)
   static\
       index.htm
       images\
           logo.jpg
       js\
           main.js
           sub.js
       css\
           app.css
       part\
           main.htm
           sub.htm

我已经阅读了构建包含静态HTML文件的.WAR文件的方法,但由于即使在单个HTML文件修改时也需要重建和重新部署WAR文件,所以该方法是不可接受的。
考虑到我的Spring知识非常有限,因此最好提供一个确切而具体的答案。

1
有什么问题无法解决吗?/static中的类路径资源默认情况下将作为静态资源提供(还包括/public和其他几个位置),因此您应该能够运行您的应用程序并加载例如/css/app.css。 - Dave Syer
@DaveSyer实际上我发现当我在MyApp目录下运行MyApp.jar时它是有效的。当我阅读org.springframework.boot.context.embedded.AbstractEmbeddedServletContainerFactory的源代码时,我验证了它。COMMON_DOC_ROOTS中有“static”。(请参见https://github.com/spring-projects/spring-boot/blob/master/spring-boot/src/main/java/org/springframework/boot/context/embedded/AbstractEmbeddedServletContainerFactory.java?source=cc)但我找不到任何关于它的文档,所以我认为将静态文件包含在WAR文件中是唯一的方法。 - zeodtr
@DaveSyer 我想要更改静态文件的目录到任何我想要的地方,而不受类路径或当前目录的限制。我的问题中的目录结构只是一个例子(恰好被框架支持)。因此,我发布了另一个问题,从另一个角度来查看这个问题。(请参见https://dev59.com/KGIj5IYBdhLWcg3wnmXA) - zeodtr
3个回答

6

你另一个问题中我看到你实际上想要的是能够更改应用程序中静态资源的路径默认值。暂且不考虑为什么你要这样做,有几个可能的解决方案。

  • 一种方法是提供一个普通的Spring MVC @Bean,类型为WebMvcConfigurerAdapter,并使用addResourceHandlers()方法添加附加路径以访问静态资源(请参阅WebMvcAutoConfiguration了解默认设置)。
  • 另一种方法是使用ConfigurableEmbeddedServletContainerFactory功能来设置servlet上下文根路径。
  • 最后一种“核选项”是提供一个类型为EmbeddedServletContainerFactory@Bean定义,以按照您需要的方式设置servlet容器。如果您使用现有的任何一种具体实现,则它们都会扩展您已经找到的Abstract*类,因此它们甚至都有一个名为documentRoot的属性设置器。您还可以使用类型为EmbeddedServletContainerCustomizer@Bean进行许多常见操作。

谢谢你的回答。所以我的另一个问题中的方法是你建议中的最后一个。现在,我会坚持它(因为它有效)。在学习更多关于Spring和Spring Boot之后,也许我会改变这种方法。 - zeodtr
1
关于我想改变静态文件的默认位置的原因,只是为了开发和部署环境的灵活性。也许我现在对Spring框架(甚至Java)的约定还不够了解。 - zeodtr
请注意,对于Spring Boot 2.x,这些类更改了名称。现在它们是ConfigurableTomcatWebServerFactoryTomcatServletWebServerFactoryTomcatServletWebServerFactoryCustomizer - Ruslan Stelmachenko

4
如果您在命令“java -jar blabla.jar”中指定了“-cp .”选项,并且当前目录中有“static”目录,则已经足够了。

我发现这个答案更简单,不需要改动代码。但是为了让它按预期工作,OP需要将其static文件夹重命名为public,因为Spring Boot(这里是v1.1.6)会在类路径中查找public文件夹。 - Pierre-David Belanger
这个解决方案非常适合前端开发,只需在IDE中添加运行时依赖项即可。然后,您就可以编辑HTML / JavaScript内容,而无需重新打包/重启后端。 - osundblad

0

看看this Dave Syer 的答案实现。

您可以使用ConfigurableEmbeddedServletContainer.setDocumentRoot(File documentRoot)设置文档根目录,Web上下文将使用该目录来提供静态文件。

工作示例:

package com.example.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import java.io.File;
import java.nio.file.Paths;

@Configuration
public class WebConfigurer implements ServletContextInitializer, EmbeddedServletContainerCustomizer {
    private final Logger log = LoggerFactory.getLogger(WebConfigurer.class);

    private final Environment env;
    private static final String STATIC_ASSETS_FOLDER_PARAM = "static-assets-folder";
    private final String staticAssetsFolderPath;

    public WebConfigurer(Environment env, @Value("${" + STATIC_ASSETS_FOLDER_PARAM + ":}") String staticAssetsFolderPath) {
        this.env = env;
        this.staticAssetsFolderPath = staticAssetsFolderPath;
    }

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        if (env.getActiveProfiles().length > 0) {
            log.info("Web application configuration, profiles: {}", (Object[]) env.getActiveProfiles());
        }
        log.info(STATIC_ASSETS_FOLDER_PARAM + ": '{}'", staticAssetsFolderPath);
    }

    private void customizeDocumentRoot(ConfigurableEmbeddedServletContainer container) {
        if (!StringUtils.isEmpty(staticAssetsFolderPath)) {
            File docRoot;
            if (staticAssetsFolderPath.startsWith(File.separator)) {
                docRoot = new File(staticAssetsFolderPath);
            } else {
                final String workPath = Paths.get(".").toUri().normalize().getPath();
                docRoot = new File(workPath + staticAssetsFolderPath);
            }
            if (docRoot.exists() && docRoot.isDirectory()) {
                log.info("Custom location is used for static assets, document root folder: {}",
                        docRoot.getAbsolutePath());
                container.setDocumentRoot(docRoot);
            } else {
                log.warn("Custom document root folder {} doesn't exist, custom location for static assets was not used.",
                        docRoot.getAbsolutePath());
            }
        }
    }

    @Override
    public void customize(ConfigurableEmbeddedServletContainer container) {
        customizeDocumentRoot(container);
    }

}

现在你可以使用命令行和配置文件(src\main\resources\application-myprofile.yml)来自定义你的应用程序:

> java -jar demo-0.0.1-SNAPSHOT.jar --static-assets-folder="myfolder"
> java -jar demo-0.0.1-SNAPSHOT.jar --spring.profiles.active=myprofile

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接