In web.xml, map directory listing URL to spring dispatcher:
<servlet-mapping> <servlet-name>spring</servlet-name> <url-pattern>*.html</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>spring</servlet-name> <url-pattern>/download/*</url-pattern> </servlet-mapping>The first one is your existing dispatcher and the second one for download.
In spring security configuration set up appropriate configuration:
<intercept-url pattern="/download/**" access="permitAll" />In Spring servlet config xml set this up to make path configurable:
<bean id="appPropertiesBean" class="org.springframework.beans.factory.config.PropertiesFactoryBean"> <property name="singleton" value="true" /> <property name="properties"> <props> <prop key="downloadPath">C:/</prop> </props> </property> </bean>
In your controller for the path property:
@Value("#{appPropertiesBean.downloadPath}") private File downloadPath;
In controller add methods to generate directory listing:
@RequestMapping(value = "/files/**", method = RequestMethod.GET)
public ModelAndView archiveDirectoryListing(HttpServletRequest request, HttpServletResponse response) {
try {
//String restOfTheUrl=(String)request.getAttribute( HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE );
//The above line seems to cause issues with filenames
// containing semicolon. Use the below line insteadString restOfTheUrl=request.getPathInfo();
restOfTheUrl=restOfTheUrl.substring("/files".length()); File physicalFileRequested=new File(downloadPath,restOfTheUrl); System.out.println("Accessing .." + physicalFileRequested); if (!physicalFileRequested.getCanonicalPath().startsWith(archivePath.getCanonicalPath())) { //Requester is trying to go above the archive directory response.setStatus(HttpServletResponse.SC_FORBIDDEN); return; } if (!physicalFileRequested.exists()) { response.setStatus(HttpServletResponse.SC_FORBIDDEN); return; } System.out.println("Access allowed " + physicalFileRequested); System.out.println("Access allowed " + physicalFileRequested.isFile()); System.out.println("Access allowed " + physicalFileRequested.isDirectory()); System.out.println("Rest of url : " + restOfTheUrl); if (physicalFileRequested.isFile()) { System.out.println("Accessing file:" + physicalFileRequested); streamFile(physicalFileRequested,response); } else if (physicalFileRequested.isDirectory()){ if (! restOfTheUrl.endsWith("/")) { response.sendRedirect(request.getContextPath() + "/download/files" + restOfTheUrl + "/"); } System.out.println("Accessing dir:" + physicalFileRequested); generateDirectoryListing(physicalFileRequested,response);
} } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("Error accessing requested file/directory."); } } private void streamFile(File physicalFileRequested, HttpServletResponse response) { try { response.setContentType("application/pdf");
// get your file as InputStream InputStream is = new FileInputStream(physicalFileRequested); // copy it to response's OutputStream IOUtils.copy(is, response.getOutputStream()); response.flushBuffer(); } catch (IOException ex) { throw new RuntimeException("Error accessing requested file/directory."); } } private void generateDirectoryListing(File physicalFileRequested, HttpServletResponse response) { try { StringBuilder sb=new StringBuilder(); sb.append("<html>"); sb.append("<body>"); sb.append("<a href='..'>..</a><br>"); File [] childFiles=physicalFileRequested.listFiles(); for (File childFile:childFiles) { String relativePath = childFile.getName(); sb.append("<a href='"+ URLEncoder.encode(relativePath,"UTF-8") + "'>" +relativePath+"</a><br>"); } sb.append("</body>"); sb.append("</html>"); response.getWriter().print(sb.toString()); } catch (Exception e) { throw new RuntimeException("Error accessing requested file/directory."); } }
Download URL will be like:
http://localhost:9090/context/download/files/
Alternatively, the directory listing HTML could be generated using a view, by using the following alternate implementation of the method (Note: wherever null is returned for ModelAndView, Spring assumes that response has already been handled and no view redirection needs to happen):
@RequestMapping(value = "/files/**", method = RequestMethod.GET) public ModelAndView archiveDirectoryListing(HttpServletRequest request, HttpServletResponse response) { try { String restOfTheUrl=(String)request.getAttribute( HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE ); restOfTheUrl=restOfTheUrl.substring("/files".length()); File physicalFileRequested=new File(archivePath,restOfTheUrl); System.out.println("Accessing .." + physicalFileRequested); if (!physicalFileRequested.getCanonicalPath().startsWith(archivePath.getCanonicalPath())) { //Requester is trying to go above the archive directory response.setStatus(HttpServletResponse.SC_FORBIDDEN); return null; } if (!physicalFileRequested.exists()) { response.setStatus(HttpServletResponse.SC_NOT_FOUND); return null; } if (physicalFileRequested.isFile()) { streamFile(physicalFileRequested,response); return null; } else if (physicalFileRequested.isDirectory()){ if (! restOfTheUrl.endsWith("/")) { response.sendRedirect(request.getContextPath() + "/archive/files" + restOfTheUrl + "/"); } System.out.println("Accessing dir:" + physicalFileRequested); ModelAndView modelAndView=new ModelAndView("direcoryListing"); modelAndView.addObject("fileList", physicalFileRequested.listFiles()); return modelAndView; } } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("Error accessing requested file/directory."); } response.setStatus(HttpServletResponse.SC_NOT_FOUND); return null; }And a freemarker view, direcoryListing.ftl:
<html> <body> <h1>Welcome to the download page</h1> <a href='..'>..</a><br> <#list fileList as file> <a href='${file.name}'> ${file.name}</a><br> </#list> </body> </html>
Configure freemarker in application context:
<!-- freemarker config --><bean id="freemarkerConfig"class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer"><property name="templateLoaderPath" value="/WEB-INF/pages/" /></bean><!-- View resolvers can also be configured with ResourceBundles or XML files.If you need different view resolving based on Locale, you have to use theresource bundle resolver. --><bean id="viewResolver"class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver"><property name="cache" value="true" /><property name="prefix" value="" /><property name="suffix" value=".ftl" /></bean>
No comments:
Post a Comment