【问题标题】:Tomcat not loading new images from context path after server is already started服务器已启动后,Tomcat 未从上下文路径加载新图像
【发布时间】:2026-01-27 21:25:01
【问题描述】:

在我的 tomcat 文件夹的server.xml 内,我在<Host> 标签下有一个虚拟文件夹:

<Context docBase="C:\app_files\" path="/app_files"/>

所以我可以通过 url 访问此文件夹中的文件:http://localhost:8080/app_files/some_file.jpg

但这仅适用于图像或文件在服务器启动之前已经存在的情况。如果我转到指向服务器启动后创建的图像的 URL,则会出现 404 错误。重启服务器后,图片加载正常。

如何解决这个问题?

【问题讨论】:

    标签: java tomcat


    【解决方案1】:

    如果您使用 Tomcat 应用程序管理器,您可以在不重新启动整个服务器的情况下取消部署/部署您的单个应用程序(并且不会影响其他 webapps),或者更残酷的是,您可以从 webapps 目录替换所需的战争(再次取消部署/部署将随之而来)。如果您必须保证应用程序的正常运行时间,即使在这种情况下,您也必须进行并行部署 (here a guide for tomcat 8)

    【讨论】:

    • 不,不想手动做任何事情,我希望 tomcat 自动显示图像而不需要做任何事情,我需要这个,因为图像是在用户执行某些操作时创建的网站,所以图像是动态创建的,然后图像应该立即显示给用户。
    • @MateusViccari 看看mulesoft.com/tcat/tomcat-reload#context -> 使用 WatchedResource 热重载
    • 不,很遗憾,它不起作用,试图将文件夹放入 WatchedResource 但服务器启动后创建的图像仍然出现 404 错误。
    【解决方案2】:

    尝试将属性 autoDeploy="true" 添加到您的上下文配置中,这将告诉 catalina 监控您的文档库位置以进行更改

    【讨论】:

    • 没用,显然这只适用于 java 类
    • @MateusViccari 是的,你是对的,检查我的编辑,我测试了它并且它有效。
    • 使用autoDeploy=true也不起作用,服务器仍然返回404
    【解决方案3】:

    我实际上设法在不使用 server.xml 上的上下文的情况下做我想做的事。 它基于BalusC's solution 通过servlet 提供静态文件 方法如下:

    • 首先,我在我的系统中创建了一个环境变量(可以在每个操作系统中完成,只需谷歌“如何在 windows、linux 等上创建环境变量”),名为 MANAGEMENT_FILES,我的变量值是 c:/management_files/
    • 然后,在创建应该向用户显示的图像的方法上,我将图像保存在此文件夹中(这是上一步中环境变量的值)
    
    public String imageUrl;
    
    public void createAndShowImage() {
        try {
            String imageName = "/nice_images_folder/cool_image.jpg";
            File imageFile = new File(System.getenv("MANAGEMENT_FILES") + imageName);
            //Here goes your logic to create the file
            createImage(imageFile);
            //Here i use a fixed URL, you can do it as you see fit
            this.imageUrl = "http://localhost:8080/MyCoolApp/" + CoolFileServlet.BASE_URL + imageName + "?delete=true";
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    • 这是您必须创建的 servlet,此 servlet 返回您放入文件夹中的图像或任何其他文件:
    
    import java.io.BufferedInputStream;
    import java.io.BufferedOutputStream;
    import java.io.Closeable;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.net.URLDecoder;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    @WebServlet(name="CoolFileServlet", urlPatterns={CoolFileServlet.BASE_URL + "*"})
    public class CoolFileServlet extends HttpServlet {
    
        public static final String BASE_URL = "/shiny_happy_files/";
    
        private static final int DEFAULT_BUFFER_SIZE = 10240;
    
        private String filePath;
    
        public void init() throws ServletException {
            this.filePath = System.getenv("MANAGEMENT_FILES");
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException
        {
            String requestedFile = request.getPathInfo();
    
            if (requestedFile == null) {
                response.sendError(HttpServletResponse.SC_NOT_FOUND); // 404.
                return;
            }
    
            File file = new File(filePath, URLDecoder.decode(requestedFile, "UTF-8"));
    
            if (!file.exists()) {
                response.sendError(HttpServletResponse.SC_NOT_FOUND); // 404.
                return;
            }
    
            String contentType = getServletContext().getMimeType(file.getName());
            if (contentType == null) {
                contentType = "application/octet-stream";
            }
    
            response.reset();
            response.setBufferSize(DEFAULT_BUFFER_SIZE);
            response.setContentType(contentType);
            response.setHeader("Content-Length", String.valueOf(file.length()));
            response.setHeader("Content-Disposition", "attachment; filename=\"" + file.getName() + "\"");
    
            BufferedInputStream input = null;
            BufferedOutputStream output = null;
    
            try {
                input = new BufferedInputStream(new FileInputStream(file), DEFAULT_BUFFER_SIZE);
                output = new BufferedOutputStream(response.getOutputStream(), DEFAULT_BUFFER_SIZE);
    
                byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
                int length;
                while ((length = input.read(buffer)) > 0) {
                    output.write(buffer, 0, length);
                }
            } finally {
                close(output);
                close(input);
                try {
                    if ("true".equals(request.getParameter("delete"))) {
                        if (!file.delete()) {
                            throw new RuntimeException("File could not be deleted");
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    
        private static void close(Closeable resource) {
            if (resource != null) {
                try {
                    resource.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
    }
    

    注意,访问时可以在url中传入参数delete=true,恢复后立即删除(以防不再需要)。

    在我的情况下,我需要在用户执行某些操作后在页面上显示图像,所以我所要做的就是显示图像 url:

    <h:graphicImage url="#{myManagedBean.imageUrl}"/>
    

    就是这样,您可以使用此 servlet 提供任何类型的文件,它会立即返回您想要的文件,并且该文件将在服务器重新启动/重新部署之间保持活动状态(如果它没有通过 delete=true.

    【讨论】:

      【解决方案4】:

      如果您喜欢不同的方法,您也可以通过在控制器中映射一个函数来执行此操作,该控制器返回 IOUtils 对象,同时指定媒体类型,然后在您的 img 的 src 中调用该函数的 URL。

      @ResponseBody
      @RequestMapping(value="/load_photo", params = {"myPhoto"}, method = RequestMethod.GET, produces = MediaType.IMAGE_PNG_VALUE)
      public byte[] loadPhoto(@RequestParam(value = "myPhoto") String myPhoto) throws IOException {
          
          File file = new File(servletContext.getRealPath("")+Constants.PATH_TO_FILE+myPhoto);
          
          FileInputStream fis = new FileInputStream(file);
          return IOUtils.toByteArray(fis);
      }
      

      然后你在你的 JSP 中调用你的 img:

      <img class="photo" src="/app/controller/load_photo?myPhoto=${myPhoto}">
      

      这样,您可以提供动态生成的图像。

      【讨论】: