Java/JSP 图片上传。在哪里保存这些图像文件?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/385332/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 14:05:32  来源:igfitidea点击:

Java/JSP Image upload. Where to keep these image files?

javajspfile-uploadstripes

提问by TRF

I am writing a simple application that lets a user upload images. After the upload, the user can tag them or remove them.

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

I figured out how to upload the files and save them once the files are uploaded. I am keeping tracking of a global path where images are kept. In the database I keep the meta data about the images like file name, tags, etc.

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

I am using Java/JSP (specifically Stripes framework but my problem is generic).

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

My question is where do I keep these image files once they are uploaded?

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

Right now I have two web applications deployed on a Tomcat server. One main web application and other one is the where I upload the images.

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

But this does not work as I can not see the uploaded images in the main application until I redeploy/restart Tomcat.

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

It seems like Tomcat does not pick newly uploaded images automatically.

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

Does anyone have any solutions?

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

This is a simple project, so I do not want to store them in a database or use Apache for images. That is all just too complicated for this small project.

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

Thanks.

谢谢。

采纳答案by Chris Johnston

Definitely don't store the images in the database, but you will want to store the image path in the database. This will allow you to store the image just about anywhere.

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

Since you are using two tomcat applications, your best bet may be to store the images outside of either app and stream the image back to the user instead of letting tomcat manage the files. Otherwise, I would ask why you are trying to do this with two web apps.

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

回答by TRF

I was using two web applications to avoid over writing the uploaded images in case I redeploy a new main application war file.

我使用了两个 Web 应用程序以避免在我重新部署新的主应用程序 war 文件时覆盖上传的图像。

But as you mention there is no other option but to stream them through a Servlet or something I guess I can keep them outside tomcat directory.

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

I wanted to avoid writing this Streaming Servlet. Just too small project to deal with all the mess (like proper content type, 404, etc.) while writing the streaming servlet.

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

回答by Adeel Ansari

However, storing uploaded images inside the web-app directory is not a wise thing to do, and you know it.

但是,将上传的图像存储在 web-app 目录中并不是一件明智的事情,您知道这一点。

By the way, you might want to look this stackoverflow thread, lately discussed where to store the images. It might not solve your issue, surely will give you more confidence on what you are doing.

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

回答by TRF

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

Ok Here is a Servlet that I quickly wrote that can stream images:

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

Here is the List of limitations and know issues:

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

  • May have XSS vulnerability use with care
  • Not production ready use as reference
  • Images need to in the web application directory. Can be easily change but I too lazy (it is not worth it the project is too small)
  • Only stream jpg,gif or png files.
  • 可能有 XSS 漏洞小心使用
  • 非生产准备使用作为参考
  • 图像需要在 web 应用程序目录中。可以随便改但是我太懒了(项目太小不值得)
  • 仅流式传输 jpg、gif 或 png 文件。

Usage:

用法:

Let say you deploy this web application called images as separate application.

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

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

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

means there should be a directory in "promotions" with image "promo.jpg" with in this images web application.

意味着在此图像 Web 应用程序中应该有一个带有图像“promo.jpg”的“promotions”目录。

PS: Do not ask why I am doing this Servlet Container only solution that sucks big time.

PS:不要问我为什么要做这个 Servlet Container only 解决方案,这很浪费时间。

回答by TRF

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

Oh ya configure your Servlet like above for best results :P

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

回答by Will Hartung

I've solved this in different ways.

我用不同的方法解决了这个问题。

First, the non-portable way, is that Glassfish (and I do believe Tomcat as well) allows you to map an external directory in to the webapps hierarchy. This works really well and does exactly what you want. It lets you store your images in an external directory away from your webapp, yet still serve them up.

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

However, this technique is not portable.

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

The way to I've done it portably is by creating a filter.

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

You place the filter someplace obvious, say "/images".

你把过滤器放在明显的地方,比如“/images”。

What the filter does is this:

过滤器的作用是这样的:

  • it checks for the image (or anything, it works with any static resource) in a special directory within the webapp. For our example we'll use the url "/webapp/images".

  • if the file DOES NOT exist, we copy the file from your external location in to the appropriate spot within the webapp. So, let's say the reqyest url is "/images/banner.gif". And that your files are stored on disk at "/home/app/images". So, our source file is "/home/app/images/banner.gif". We then copy it to where we want it in the webapp tree. We use "ServletContext.getRealPath" for this. So, the destination will be "ServletContext.get RealPath("/webapp/images/banner.gif"). Just copy the source to the destination.

  • if the file already existed, or now exists, simply forward to the actual image at /webapp/images/banner.gif.
  • 它在 web 应用程序的一个特殊目录中检查图像(或任何东西,它适用于任何静态资源)。对于我们的示例,我们将使用 url“/webapp/images”。

  • 如果文件不存在,我们会将文件从您的外部位置复制到 web 应用程序中的适当位置。所以,假设最需要的 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 中的实际图像。

Effectively you end up having a file cache within your webapps deployment tree. The down side is that it's a cache, so it needs to be maintained (i.e. you should check if the original is newer than your cache, make sure you delete if the source is deleted, etc.). Also, it duplicates your resources, so your images will consume, eventually, twice as much disk space. Finally, there's the initial copy cost at start up.

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

However, it DOES work, and it prevents you from having to serve up static resources using your own code. (Which is the 3rd solution, map a filter/servlet to intercept the URLs and simply stream it your self.)

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

I would look at the construct within Tomcat (assuming it exists) to do the mapping for you. I know it exists in Glassfish. (Google alternatedocroot for Glassfish to see how it works.)

我会查看 Tomcat 中的构造(假设它存在)来为您进行映射。我知道它存在于 Glassfish 中。(Glassfish 的 Google 替代文档根目录,看看它是如何工作的。)