9

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");
    }
}
Mel
  • 5,837
  • 10
  • 37
  • 42
Sankar
  • 91
  • 1
  • 2
  • 1
    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- Jan 26 '16 at 00:13
  • 1
    You better write a web page which list all the images. – pmverma Jan 26 '16 at 11:30
  • 1
    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 Jan 26 '16 at 14:56

3 Answers3

2

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);

    }
}
Hobo Joe
  • 125
  • 1
  • 7
0

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.

Community
  • 1
  • 1
Mark McLaren
  • 11,470
  • 2
  • 48
  • 79
  • While the debugger shows the property was reassigned unfortunately it does not affect the listing. Perhaps the way around is to set the error handler and make listing manually generated. – Sasha Firsov Aug 16 '17 at 23:56
0

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