8
votes

I have a spring boot application with embedded Tomcat. I wanted to expose some images files & folders from a different location via tomcat directory listing. So I added the below in my configuration file called

public class AppConfig extends WebMvcConfigurerAdapter

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

I can now access individual image(s), if I know the name.

Example: localhost:8080/images/file.jpg.

But since the directory listing is false by default, I can't access the images listing through "localhost:8080/images/" to know the all the available images.

I tried the below option to add the listings as well, but did not work.

public class MyApplication implements ServletContextInitializer{

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

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        servletContext.setInitParameter("listings", "true");
    }
}
3
I don't believe the resource handler supports directory listings. In particular, it may not always be practical to identify the contents of a directory, since resources might be scattered among several classpath jars.chrylis -cautiouslyoptimistic-
You better write a web page which list all the images.pmverma
Thanks for your comments. You are right Chrylis, the resource handler doesn't support directory listing, but it was used to map an external image path to /images.Sankar

3 Answers

1
votes

Updated for Spring 2.1

import org.apache.catalina.Context;
import org.apache.catalina.Wrapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.embedded.tomcat.TomcatContextCustomizer;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.stereotype.Component;

@Component
public class MyTomcatWebServerCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory>  {

    @Value("${tomcat.file.base}")  // C:\\some\\parent\\child
    String tomcatBaseDir;

    @Override
    public void customize(TomcatServletWebServerFactory factory) {
        // customize the factory here
        TomcatContextCustomizer tomcatContextCustomizer = new TomcatContextCustomizer() {
            @Override
            public void customize(Context context) {
                String parentFolder = tomcatBaseDir.substring(0,tomcatBaseDir.lastIndexOf("\\"));
                String childFolder = tomcatBaseDir.substring(tomcatBaseDir.lastIndexOf("\\") + 1);
                context.setDocBase(parentFolder);
                Wrapper defServlet = (Wrapper) context.findChild("default");
                defServlet.addInitParameter("listings", "true");
                defServlet.addInitParameter("readOnly", "false");
                defServlet.addMapping("/"+ childFolder + "/*");
            }
        };
        factory.addContextCustomizers(tomcatContextCustomizer);

    }
}
0
votes

In an identical way to SpringBoot Embedded Tomcat JSPServlet Options you can use an EmbeddedServletContainerCustomizer @Bean to look up the default servlet and configure its init parameters.

@Bean
public EmbeddedServletContainerCustomizer customizer() {
    return new EmbeddedServletContainerCustomizer() {

        @Override
        public void customize(ConfigurableEmbeddedServletContainer container) {
            if (container instanceof TomcatEmbeddedServletContainerFactory) {
                customizeTomcat((TomcatEmbeddedServletContainerFactory) container);
            }
        }

        private void customizeTomcat(TomcatEmbeddedServletContainerFactory tomcat) {
            tomcat.addContextCustomizers(new TomcatContextCustomizer() {

                @Override
                public void customize(Context context) {
                    Wrapper defServlet = (Wrapper) context.findChild("default");
                    defServlet.addInitParameter("listings", "true");
                }
            });
        }
    };
}

Kudos to Andy Wilkinson.

0
votes

In springboot /** is mapped to ResourceHttpRequestHandler. The call never gets delegated to DefaultServlet for the listings to take effect. I had to make two more adjustments to Mark's solution get it to work.

  1. Add a different mapping to the DefaultServlet -> "/static/*"
  2. The docbase from where the static contents are served is a tmp folder. I had to set it to the folder where the static contents are present.

            public void customize(Context context) {
                context.setDocBase("../../../mytest");
                Wrapper defServlet = (Wrapper) context.findChild("default");
                defServlet.addInitParameter("listings", "true");
                defServlet.addInitParameter("readOnly", "false");
                defServlet.addMapping("/static/*");
            }
    

    Deployment folder structure
    /myhome/mytest
    ----myapp.jar
    ----/tomcat/webapps
    ----/static
    --------All static files go here

    application.yml
    server :
     tomcat :
      basedir : tomcat

    Current working dir to run the app /myhome/mytest

    url to test : http://localhost:8080/static