Serving Static resources from file system | Spring

2020-02-08 05:41发布

问题:

Using a Spring Boot web application I trying to serve my static resource from a file system folder outside my project.

Folder structure looks like:-

          src
             main
                 java
                 resources
             test
                 java
                 resources
          pom.xml
          ext-resources   (I want to keep my static resources here)
                 test.js

Spring Configuration:-

@SpringBootApplication
public class DemoStaticresourceApplication extends WebMvcConfigurerAdapter {

    public static void main(String[] args) {
        SpringApplication.run(DemoStaticresourceApplication.class, args);
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/test/**").addResourceLocations("file:///./ext-resources/")
                .setCachePeriod(0);
    }
}

Hitting 'http://localhost:9999/test/test.js' in my browser gives back a 404.

How should I configure ResourceHandlerRegistry to serve static resources from the above mentioned 'ext-resources' folder?

I should be able to switch cache on/off for dev/prod environment.

Thanks

UPDATE 1

Giving absolute file path works:-

@Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/test/**")
                .addResourceLocations(
                        "file:///C:/Sambhav/Installations/workspace/demo-staticresource/ext-resources/")
                .setCachePeriod(0);
}

How can I provide relative location? Absolute path will make my life tough during build & deploy process.

回答1:

file:/// is an absolute URL pointing to the root of the filesystem and, therefore, file:///./ext-resources/ means that Spring Boot is looking for resources in a directory named ext-resources in the root.

Update your configuration to use something like file:ext-resources/ as the URL.



回答2:

This is what I did in the WebConfig class, inside the addResourceHandlers method:

boolean devMode = this.env.acceptsProfiles("development");
String location;
if (devMode) {
    String currentPath = new File(".").getAbsolutePath();
    location = "file:///" + currentPath + "/client/src/";
} else {
    location = "classpath:static/";
}


回答3:

Spring Boot Maven Plugin can add extra directories to the classpath. In your case you could include that in your pom.

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <version>${spring.boot.version}</version>
    <configuration>
        <folders>
            <folder>${project.build.directory}/../ext-resources</folder>
        </folders>

        ...
    </configuration>
</plugin>

So that way you don't need inlcude any hard-code in your classes. Simply start your webapp with

mvn spring-boot:run


回答4:

All solutions provided with Spring Boot 2 didn't work in our case, not with the WebMvcConfigurerAdapter nor with the WebMvcConfigurer. Using the @EnableWebMvc annotation made it even worse as the regular contents that worked before stopped working also due to the fact that the WebMvcAutoConfiguration was getting ignored at that point I suppose.

One possible solution is to define the spring.resources.static-locations property, but this implies that the static location is hard coded. We wanted to add a static location at runtime so it is independent of the environment we're deploying to as the external directoy containing the resource was located at the same place as where the application is deployed. To achieve this I came up with the following solution:

@Configuration
@SpringBootApplication
public class MainConfiguration {
    @Inject
    public void appendExternalReportingLocation(ResourceProperties resourceProperties) {
        String location = "file://" + new File("ext-resources").getAbsolutePath();
        List<String> staticLocations = newArrayList(resourceProperties.getStaticLocations());
        staticLocations.add(location);
        resourceProperties.setStaticLocations(staticLocations.toArray(new String[staticLocations.size()]));
    }
}

UPDATE: Unfortunately the above solution only worked when launching the Spring Boot application from within the IDE (e.g. IntelliJ). So I came up with another solution to serve static content from the file system.

First I created a Filter as follow:

public class StaticContentFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        File file = new File(new File("ext-resources").getAbsolutePath(), ((HttpServletRequest)request).getServletPath());
        if (file.exists() && !file.isDirectory()) {
            org.apache.commons.io.IOUtils.copy(new FileInputStream(file), response.getOutputStream());
        }
        else {
            chain.doFilter(request, response);
        }
    }
}

Then I registerd with Spring Boot as follow:

@Configuration
@SpringBootApplication
public class MainConfiguration {
    @Bean
    public FilterRegistrationBean staticContentFilter() {
        return new FilterRegistrationBean(new StaticContentFilter());
    }
}