5

我正在编写一个简单的应用程序,让用户上传图像。上传后,用户可以标记或删除它们。

我想出了如何上传文件并在文件上传后保存它们。我正在跟踪保存图像的全局路径。在数据库中,我保留有关图像的元数据,例如文件名、标签等。

我正在使用 Java/JSP(特别是 Stripes 框架,但我的问题是通用的)。

我的问题是这些图像文件上传后我应该保存在哪里?

现在,我在 Tomcat 服务器上部署了两个 Web 应用程序。一个主要的网络应用程序和另一个是我上传图像的地方。

但这不起作用,因为在我重新部署/重新启动 Tomcat 之前,我无法在主应用程序中看到上传的图像。

似乎 Tomcat 不会自动选择新上传的图像。

有没有人有任何解决方案?

这是一个简单的项目,所以我不想将它们存储在数据库中或将 Apache 用于图像。对于这个小项目来说,这太复杂了。

谢谢。

4

6 回答 6

5

绝对不要将图像存储在数据库中,但您会希望将图像路径存储在数据库中。这将允许您将图像存储在几乎任何地方。

由于您正在使用两个 tomcat 应用程序,因此最好的选择可能是将图像存储在任一应用程序之外并将图像流式传输回给用户,而不是让 tomcat 管理文件。否则,我会问您为什么要尝试使用两个 Web 应用程序来执行此操作。

于 2008-12-22T01:49:06.560 回答
3

但是,将上传的图像存储在 web-app 目录中并不是一个明智的做法,而且您知道这一点。

顺便说一句,你可能想看看这个stackoverflow 线程,最近讨论了在哪里存储图像。它可能无法解决您的问题,但肯定会让您对自己正在做的事情更有信心。

于 2008-12-22T02:05:45.807 回答
2

我已经以不同的方式解决了这个问题。

首先,不可移植的方式是 Glassfish(我也相信 Tomcat)允许您将外部目录映射到 webapps 层次结构。这非常有效,并且完全符合您的要求。它使您可以将图像存储在远离您的 web 应用程序的外部目录中,但仍然可以提供它们。

然而,这种技术是不可移植的。

我以便携方式完成它的方法是创建一个过滤器。

您将过滤器放置在明显的地方,例如“/images”。

过滤器的作用是:

  • 它在 webapp 的一个特殊目录中检查图像(或任何东西,它适用于任何静态资源)。对于我们的示例,我们将使用 URL“/webapp/images”。

  • 如果文件不存在,我们会将文件从您的外部位置复制到 webapp 中的适当位置。所以,假设最需要的 url 是“/images/banner.gif”。并且您的文件存储在“/home/app/images”的磁盘上。所以,我们的源文件是“/home/app/images/banner.gif”。然后我们将它复制到 webapp 树中我们想要的位置。我们为此使用“ServletContext.getRealPath”。因此,目标将是“ServletContext.get RealPath("/webapp/images/banner.gif")。只需将源复制到目标即可。

  • 如果文件已经存在或现在存在,只需转发到 /webapp/images/banner.gif 中的实际图像。

实际上,您最终会在您的 webapps 部署树中拥有一个文件缓存。不利的一面是它是一个缓存,因此需要对其进行维护(即,您应该检查原始文件是否比您的缓存新,如果源被删除,请确保删除,等等)。此外,它会复制您的资源,因此您的图像最终会消耗两倍的磁盘空间。最后,还有启动时的初始复制成本。

但是,它确实有效,并且它使您不必使用自己的代码提供静态资源。(这是第 3 个解决方案,映射一个过滤器/servlet 以拦截 URL 并简单地自己流式传输。)

我会查看 Tomcat 中的构造(假设它存在)来为您进行映射。我知道它存在于 Glassfish 中。(谷歌 Alternatedocroot for Glassfish 看看它是如何工作的。)

于 2008-12-22T04:54:59.553 回答
0

我正在使用两个 Web 应用程序来避免过度写入上传的图像,以防我重新部署一个新的主应用程序 war 文件。

但是正如您提到的,除了通过 Servlet 或其他方式流式传输它们之外别无选择,我想我可以将它们保留在 tomcat 目录之外。

我想避免编写这个 Streaming Servlet。在编写流 servlet 时,项目太小而无法处理所有混乱(如正确的内容类型、404 等)。

于 2008-12-22T02:00:19.017 回答
0
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Image streaming Servlet.
 */
public class ImageDisplayServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    /**
     * @see HttpServlet#HttpServlet()
     */
    public ImageDisplayServlet() {
        super();
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String relativePath = trimToEmpty(request.getPathInfo());

        // Make sure no one try to screw with us. 
        // This is important as user can literally access any file if we are not careful
        if(isXSSAttack(relativePath) == false) {
            String pathToFile = this.getServletContext().getRealPath(request.getPathInfo());
            File file = new File(pathToFile);

            System.out.println("Looking for file " + file.getAbsolutePath());

            // show a 404 page
            if(!file.exists() || !file.isFile()) {
                httpError(404, response);
            } else {
                try {
                    streamImageFile(file, response);
                } catch(Exception e) {
                    // Tell the user there was some internal server error.\
                    // 500 - Internal server error.
                    httpError(500, response);
                    e.printStackTrace();
                }
            }
        } else {
            // what to do if i think it is a XSS attack ?!?
        }
    }

    private void streamImageFile(File file, HttpServletResponse response) {
        // find the right MIME type and set it as content type
        response.setContentType(getContentType(file));
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        try {
            response.setContentLength((int) file.length());

            // Use Buffered Stream for reading/writing.
            bis = new BufferedInputStream(new FileInputStream(file));
            bos = new BufferedOutputStream(response.getOutputStream());

            byte[] buff = new byte[(int) file.length()];
            int bytesRead;

            // Simple read/write loop.
            while (-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
                bos.write(buff, 0, bytesRead);
            }
        } catch (Exception e) {

            throw new RuntimeException(e);
        } finally {
            if (bis != null) {
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                    // To late to do anything about it now, we may have already sent some data to user.
                }
            }
            if (bos != null) {
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                    // To late to do anything about it now, we may have already sent some data to user.
                }
            }
        } 
    }

    private String getContentType(File file) {
        if(file.getName().length() > 0) {
            String[] parts = file.getName().split("\\.");
            if(parts.length > 0) {
                // only last part interests me
                String extention = parts[parts.length - 1];
                if(extention.equalsIgnoreCase("jpg")) {
                    return "image/jpg";
                } else if(extention.equalsIgnoreCase("gif")) {
                    return "image/gif"; 
                } else if(extention.equalsIgnoreCase("png")) {
                    return "image/png";
                }
            }
        }
        throw new RuntimeException("Can not find content type for the file " +  file.getAbsolutePath());
    }

    private String trimToEmpty(String pathInfo) {
        if(pathInfo == null) {
            return "";
        } else {
            return pathInfo.trim();
        }
    }

    private void httpError(int statusCode, HttpServletResponse response) {
        try {
            response.setStatus(statusCode);
            response.setContentType("text/html");
            PrintWriter writer = response.getWriter();
            writer.append("<html><body><h1>Error Code: " + statusCode + "</h1><body></html>");
            writer.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private boolean isXSSAttack(String path) {
        boolean xss = false;
        // Split on the bases of know file separator
        String[] parts = path.split("/|\\\\");

        // Now verify that no part contains anything harmful
        for(String part : parts) {
            // No double dots .. 
            // No colons :
            // No semicolons ;
            if(part.trim().contains("..") || part.trim().contains(":") || part.trim().contains(";")) {
                // Fire in the hole!
                xss = true;
                break;
            }
        }
        return xss;
    }

    /**
     * @see HttpServlet#doPost(Ht/promotions/some.jpgtpServletRequest request, HttpServletResponse response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}

好的,这是我快速编写的一个可以流式传输图像的 Servlet:

以下是限制和已知问题列表:

  • 小心使用 XSS 漏洞
  • 非生产准备使用作为参考
  • 图像需要在 web 应用程序目录中。可以很容易改变但我太懒了(项目太小不值得)
  • 仅流式传输 jpg、gif 或 png 文件。

用法:

假设您将这个名为图像的 Web 应用程序部署为单独的应用程序。

http://www.example.com/images/promotions/promo.jpg

表示在此图像 Web 应用程序中应该有一个带有图像“promo.jpg”的“促销”目录。

PS:不要问我为什么要做这个 Servlet Container 唯一的解决方案,这很糟糕。

于 2008-12-22T03:46:29.863 回答
0
  <servlet>
    <description></description>
    <display-name>ImageDisplayServlet</display-name>
    <servlet-name>ImageDisplayServlet</servlet-name>
    <servlet-class>com.example.images.ImageDisplayServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>ImageDisplayServlet</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>

哦,你像上面一样配置你的 Servlet 以获得最佳效果:P

于 2008-12-22T03:47:50.453 回答