Java/JSP 图片上传。这些图像文件保存在哪里?
-
23-08-2019 - |
题
我正在编写一个简单的应用程序,让用户上传图像。上传后,用户可以标记它们或删除它们。
我弄清楚了如何上传文件并在文件上传后保存它们。我正在跟踪保存图像的全局路径。在数据库中,我保存有关图像的元数据,例如文件名、标签等。
我正在使用 Java/JSP(特别是 Stripes 框架,但我的问题是通用的)。
我的问题是这些图像文件上传后应该保存在哪里?
现在我在 Tomcat 服务器上部署了两个 Web 应用程序。一个主要的网络应用程序和另一个是我上传图像的地方。
但这不起作用,因为在重新部署/重新启动 Tomcat 之前我无法在主应用程序中看到上传的图像。
Tomcat 似乎不会自动选择新上传的图像。
有人有解决办法吗?
这是一个简单的项目,所以我不想将它们存储在数据库中或使用 Apache 来存储图像。对于这个小项目来说,这一切都太复杂了。
谢谢。
解决方案
绝对不将图像存储在数据库中,但你将要存储在数据库中的图像路径。这将允许你存储的图像几乎任何地方。
由于使用两个tomcat的应用程序,你最好的选择可能是将图像存储或者应用程序之外,并流中的图像返回给用户,而不是让tomcat的管理文件。否则,我会问,为什么你想有两个Web应用程序来做到这一点。
其他提示
然而,存储在web-app目录中上传的图片是不是一个明智的做法,你知道它。
顺便说一句,你可能想看看这个计算器螺纹,最近讨论在哪里存储图像。它可能不是解决您的问题,必将给你更多的信心,你在做什么。
我用不同的方式解决了这个问题。
首先,不可移植的方式是 Glassfish(我也相信 Tomcat)允许您将外部目录映射到 webapps 层次结构中。这非常有效并且完全符合您的要求。它允许您将图像存储在远离网络应用程序的外部目录中,但仍然可以提供它们。
然而,该技术不可移植。
我便携地完成此操作的方法是创建一个过滤器。
您将过滤器放置在明显的地方,例如“/images”。
过滤器的作用是这样的:
它会检查 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 中的实际图像。
实际上,您最终会在 Web 应用程序部署树中拥有一个文件缓存。缺点是它是一个缓存,因此需要维护(即您应该检查原始文件是否比您的缓存更新,如果源被删除,请确保删除,等等)。此外,它还会复制您的资源,因此您的图像最终将消耗两倍的磁盘空间。最后,还有启动时的初始复制成本。
然而,它确实有效,并且它使您不必使用自己的代码来提供静态资源。(这是第三种解决方案,映射一个过滤器/servlet 来拦截 URL,然后自行流式传输。)
我会查看 Tomcat 中的构造(假设它存在)来为您进行映射。我知道它存在于 Glassfish 中。(谷歌 Glassfish 的备用文档根目录,看看它是如何工作的。)
我是用两个Web应用程序,以避免在写的情况下,我重新部署一个新的主应用程序war文件上传的图片。
不过,你提到有没有其他选择,只能通过servlet或东西,我想我可以让他们外面tomcat目录,以串流。
我想避免写这个流的Servlet。只是过小的项目来处理所有的烂摊子(如适当的内容类型,404等),而写作流的servlet。
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
意味着“promotions”中应该有一个目录,其中包含图像“promo.jpg”,并且在此图像 Web 应用程序中。
附:不要问我为什么要做这个仅 Servlet 容器的解决方案,这会浪费很多时间。
<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