如何使用JSP / Servlet将文件上传到服务器? [英] How to upload files to server using JSP/Servlet?
问题描述
如何使用JSP / Servlet将文件上传到服务器?我试过这个:
$ b
< form action =uploadmethod =post>
< input type =textname =description/>
< input type =filename =file/>
< input type =submit/>
< / form>
但是,我只获取文件名,而不是文件内容。当我向< form>
添加 enctype =multipart / form-data
,那么 request.getParameter()
返回 null
。
在研究期间,我偶然发现了 Apache Common FileUpload 。我试过这个:
pre $ File_temFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
List items = upload.parseRequest(request); //这条线是死的地方
不幸的是,servlet抛出一个没有明确信息和原因的异常。这里是堆栈跟踪:
pre $ SEVERE:servlet UploadServlet的Servlet.service()抛出异常
javax.servlet.ServletException :Servlet执行在org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:313)上抛出异常
在org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:在org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233 206)
)
在org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)$ b在org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
$ b在org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
。在组织.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
在org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298)
在org.apache.coyote .http11.Http 11Processor.process(Http11Processor.java:852)
在org.apache.coyote.http11.Http11Protocol $ Http11ConnectionHandler.process(Http11Protocol.java:588)
。在org.apache.tomcat.util.net。 JIoEndpoint $ Worker.run(JIoEndpoint.java:489)$ b $在java.lang.Thread.run(Thread.java:637)
简介
要浏览并选择要上传的文件,您需要HTML < input type =file>
字段。正如 HTML规范所述,您必须使用 POST
方法,并且必须将表单的 enctype
属性设置为multipart / form-data
。
$ b
< form action =uploadmethod =post enctype =multipart / form-data>
< input type =textname =description/>
< input type =filename =file/>
< input type =submit/>
< / form>
提交这样一个表单后,二进制多部分表单数据可以在不同的格式比<$ c没有设置$ c> enctype 。
在Servlet 3.0之前,Servlet API本身不支持 multipart /格式数据
。它仅支持 application / x-www-form-urlencoded
的默认表单enctype。使用多部分表单数据时, request.getParameter()
和配置都将返回 null
。这就是众所周知的 Apache Commons FileUpload 出现的地方。
不要手动解析!
理论上可以根据 ServletRequest#getInputStream()
一>。然而,这是一个精确而乏味的工作,需要精确的了解 RFC2388 。你不应该试图自己做这个,或者复制一些在互联网上其他地方找到的本地无库代码。许多在线资源在这方面都失败了,比如roseindia.net。另请参阅上传pdf文件。你应该使用一个真正的图书馆,这个图书馆已经被数百万用户使用(并且被隐式测试!)了好多年。当你已经在Servlet 3.0或更新的版本上时,使用本地API
如果您至少使用Servlet 3.0(Tomcat 7,Jetty 9,JBoss AS 6,GlassFish 3等),那么您可以使用标准的API提供 HttpServletRequest#getPart()
收集单独的多部分表单数据项(大多数Servlet 3.0的实现实际上使用Apache Commons FileUpload在这个掩护下!)。另外,通常的表单字段可以通过 getParameter()
通常的方式获得。首先用 @MultipartConfig
为了让它识别并支持 multipart / form-data
请求,从而得到 getPart()
:
@WebServlet(/ upload)
@MultipartConfig
public class UploadServlet extends HttpServlet {
// ...
}
@WebServlet(/ upload)
@MultipartConfig
public class UploadServlet extends HttpServlet {
// ...
}
然后,执行 doPost()
如下:
$ b
protected void doPost(HttpServletRequest请求,HttpServletResponse响应)抛出ServletException,IOException {
String description = request.getParameter(description); //检索< input type =textname =description>
Part filePart = request.getPart(file); //获取< input type =filename =file>
String fileName = Paths.get(filePart.getSubmittedFileName())。getFileName()。toString(); // MSIE修复
InputStream fileContent = filePart.getInputStream();
// ...(在这里做你的工作)
}
Path#getFileName()
。这是获取文件名的MSIE修复程序。这个浏览器错误地发送完整的文件路径,而不是只有文件名。
如果你有一个< input type =文件name =filemultiple =true/>
为多文件上传,收集如下(不幸的是,没有这样的方法 request.getParts (HttpServletRequest请求,HttpServletResponse响应)抛出ServletException异常,请参阅:http://www.microsoft.com/downloads/details.aspx?displaylang=zh-cn&displaylang=zh-cn反过来, IOException {
// ...
List< Part> fileParts = request.getParts()。stream()。filter(part - >file.equals(part.getName()))。collect(Collectors.toList()); //检索< input type =filename =filemultiple =true> (Part filePart:fileParts){
String fileName = Paths.get(filePart.getSubmittedFileName())。getFileName()。toString();
// MSIE修复
InputStream fileContent = filePart.getInputStream();
// ...(在这里做你的工作)
}
}
当你不在Servlet 3.1中时,手动获取提交的文件名
请注意 Part#getSubmittedFileName()
是在Servlet 3.1中引入(Tomcat 8,Jetty 9,WildFly 8,GlassFish 4等)。如果你还没有使用Servlet 3.1,那么你需要额外的实用方法来获取提交的文件名。
private static String(String)cd:part.getHeader(content-disposition)。split(;)){
if(cd.trim()。startsWith(文件名)){
String fileName = cd.substring(cd.indexOf('=')+ 1).trim().exc(\,);
return fileName .substring(fileName.lastIndexOf('/')+ 1).substring(fileName.lastIndexOf('\\\')+ 1); // MSIE fix。
}
}
返回null;
}
String fileName = getSubmittedFileName(filePart);
注意MSIE修正以获取文件名,这个浏览器错误地发送完整的文件路径而不是文件名。
当你不在Servlet 3.0上时,使用Apache Commons FileU pload
如果您还没有使用Servlet 3.0(是不是要升级?),通常的做法是使用 Apache Commons FileUpload 来解析多部分表单数据请求。它有一个很好的用户指南和常见问题(仔细检查两者)。还有O'Reilly( cos ) MultipartRequest
,但它有一些(小的)错误,并且不再被主动维护多年。我不会推荐使用它。 Apache Commons FileUpload仍然在积极维护当前非常成熟。为了使用Apache Commons FileUpload,您至少需要在您的webapp的 / WEB-INF / lib
:
-
commons-fileupload.jar
-
commons-io.jar $ c $
!)
您最初的尝试失败的可能性很大,因为您忘记了公用IO。
下面是一个开始的例子,您的
UploadServlet $ c>的
doPost()
当使用Apache Commons FileUpload时,$ c>可能看起来像:
$ b pre class =lang -java prettyprint-override>protected void doPost(HttpServletRequest request, HttpServletResponse响应)抛出ServletException,IOException {
尝试{
List< FileItem> items = new ServletFileUpload(new DiskFileItemFactory())。parseRequest(request);
for(FileItem item:items){
if(item.isFormField()){
//处理常规表单字段(input type =text | radio | checkbox | etc等等)。
String fieldName = item.getFieldName();
String fieldValue = item.getString();
// ...(在这里做你的工作)
} else {
//处理表单文件字段(input type =file)。
String fieldName = item.getFieldName();
String fileName = FilenameUtils.getName(item.getName());
InputStream fileContent = item.getInputStream();
// ...(在这里做你的工作)
$ b $ catch(FileUploadException e){
throw new ServletException(Can not parse multipart request。 ,e);
}
// ...
}
调用
getParameter()
,getParameterMap()
,getParameterValues()
,getInputStream()
,getReader()
事先要求。否则,servlet容器将读取并解析请求体,从而Apache Commons FileUpload将得到一个空的请求体。另见a.o. ServletFileUpload#parseRequest(request)返回一个空的列表。
请注意
FilenameUtils#getName()
。这是一个MSIE修复程序,以获取文件的名称。这个浏览器错误地发送完整的文件路径,而不是只有文件名。
另外,你也可以把它全部包装在
Filter
它会自动解析它,并将这些东西放回到请求的参数映射中,以便继续使用request.getParameter()
通过request.getAttribute()
来检索上传的文件。 您可以在此博客文章中找到示例。
GlassFish3的解决方法
getParameter()
仍然返回null
请注意,早于3.1.2的Glassfish版本的一个bug 其中
bgetParameter()
仍然返回null
。如果你的目标是这样一个容器,并且不能升级它,那么你需要在这个实用工具的帮助下从getPart()
中提取值:
$ bprivate static String getValue(Part part)throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(part.getInputStream(), UTF-8\" ));
StringBuilder value = new StringBuilder();
char [] buffer = new char [1024];
for(int length = 0;(length = reader.read(buffer))> 0;){
value.append(buffer,0,length);
}
返回value.toString();
String description = getValue(request.getPart(description)); //检索< input type =textname =description>
保存上传的文件(不要使用
getRealPath()
part.write()
有关详细信息,请正确保存获取
InputStream
(上面的代码片段中显示的fileContent
变量)到磁盘或数据库:
li> 如何将Part转换为Blob,以便将其存储在MySQL中?
服务uplo
前往以下答案,详细了解如何正确地将保存的文件从磁盘或数据库返回给客户端:
- 使用< h:graphicImage>从外部webapps / webcontext / deploy文件夹加载图像。或< img>标记
- //stackoverflow.com/questions/1812244/simplest-way-to-serve-static-data-from-outside-the-application-server-in-a-java/1812356#1812356\">最简单的方法来提供静态数据在Java Web应用程序的应用程序服务器之外
- 支持HTTP缓存的静态资源servlet的抽象模板
Ajax化表单
请教如何使用Ajax(和jQuery)上传以下答案。请注意,收集表单数据的servlet代码不需要为此更改!只有你的回应方式可能会改变,但这是相当微不足道的(而不是转发到JSP,只是打印一些JSON或XML,甚至纯文本取决于任何负责Ajax调用的脚本所期望的)。 >
希望这有助于:)
How can I upload files to server using JSP/Servlet? I tried this:
<form action="upload" method="post">
<input type="text" name="description" />
<input type="file" name="file" />
<input type="submit" />
</form>
However, I only get the file name, not the file content. When I add enctype="multipart/form-data"
to the <form>
, then request.getParameter()
returns null
.
During research I stumbled upon Apache Common FileUpload. I tried this:
FileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
List items = upload.parseRequest(request); // This line is where it died.
Unfortunately, the servlet threw an exception without a clear message and cause. Here is the stacktrace:
SEVERE: Servlet.service() for servlet UploadServlet threw exception
javax.servlet.ServletException: Servlet execution threw an exception
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:313)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:852)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:588)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
at java.lang.Thread.run(Thread.java:637)
Introduction
To browse and select a file for upload you need a HTML <input type="file">
field in the form. As stated in the HTML specification you have to use the POST
method and the enctype
attribute of the form has to be set to "multipart/form-data"
.
<form action="upload" method="post" enctype="multipart/form-data">
<input type="text" name="description" />
<input type="file" name="file" />
<input type="submit" />
</form>
After submitting such a form, the binary multipart form data is available in the request body in a different format than when the enctype
isn't set.
Before Servlet 3.0, the Servlet API didn't natively support multipart/form-data
. It supports only the default form enctype of application/x-www-form-urlencoded
. The request.getParameter()
and consorts would all return null
when using multipart form data. This is where the well known Apache Commons FileUpload came into the picture.
Don't manually parse it!
You can in theory parse the request body yourself based on ServletRequest#getInputStream()
. However, this is a precise and tedious work which requires precise knowledge of RFC2388. You shouldn't try to do this on your own or copypaste some homegrown library-less code found elsewhere on the Internet. Many online sources have failed hard in this, such as roseindia.net. See also uploading of pdf file. You should rather use a real library which is used (and implicitly tested!) by millions of users for years. Such a library has proven its robustness.
When you're already on Servlet 3.0 or newer, use native API
If you're using at least Servlet 3.0 (Tomcat 7, Jetty 9, JBoss AS 6, GlassFish 3, etc), then you can just use standard API provided HttpServletRequest#getPart()
to collect the individual multipart form data items (most Servlet 3.0 implementations actually use Apache Commons FileUpload under the covers for this!). Also, normal form fields are available by getParameter()
the usual way.
First annotate your servlet with @MultipartConfig
in order to let it recognize and support multipart/form-data
requests and thus get getPart()
to work:
@WebServlet("/upload")
@MultipartConfig
public class UploadServlet extends HttpServlet {
// ...
}
Then, implement its doPost()
as follows:
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String description = request.getParameter("description"); // Retrieves <input type="text" name="description">
Part filePart = request.getPart("file"); // Retrieves <input type="file" name="file">
String fileName = Paths.get(filePart.getSubmittedFileName()).getFileName().toString(); // MSIE fix.
InputStream fileContent = filePart.getInputStream();
// ... (do your job here)
}
Note the Path#getFileName()
. This is a MSIE fix as to obtaining the file name. This browser incorrectly sends the full file path along the name instead of only the file name.
In case you have a <input type="file" name="file" multiple="true" />
for multi-file upload, collect them as below (unfortunately there is no such method as request.getParts("file")
):
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// ...
List<Part> fileParts = request.getParts().stream().filter(part -> "file".equals(part.getName())).collect(Collectors.toList()); // Retrieves <input type="file" name="file" multiple="true">
for (Part filePart : fileParts) {
String fileName = Paths.get(filePart.getSubmittedFileName()).getFileName().toString(); // MSIE fix.
InputStream fileContent = filePart.getInputStream();
// ... (do your job here)
}
}
When you're not on Servlet 3.1 yet, manually get submitted file name
Note that Part#getSubmittedFileName()
was introduced in Servlet 3.1 (Tomcat 8, Jetty 9, WildFly 8, GlassFish 4, etc). If you're not on Servlet 3.1 yet, then you need an additional utility method to obtain the submitted file name.
private static String getSubmittedFileName(Part part) {
for (String cd : part.getHeader("content-disposition").split(";")) {
if (cd.trim().startsWith("filename")) {
String fileName = cd.substring(cd.indexOf('=') + 1).trim().replace("\"", "");
return fileName.substring(fileName.lastIndexOf('/') + 1).substring(fileName.lastIndexOf('\\') + 1); // MSIE fix.
}
}
return null;
}
String fileName = getSubmittedFileName(filePart);
Note the MSIE fix as to obtaining the file name. This browser incorrectly sends the full file path along the name instead of only the file name.
When you're not on Servlet 3.0 yet, use Apache Commons FileUpload
If you're not on Servlet 3.0 yet (isn't it about time to upgrade?), the common practice is to make use of Apache Commons FileUpload to parse the multpart form data requests. It has an excellent User Guide and FAQ (carefully go through both). There's also the O'Reilly ("cos") MultipartRequest
, but it has some (minor) bugs and isn't actively maintained anymore for years. I wouldn't recommend using it. Apache Commons FileUpload is still actively maintained and currently very mature.
In order to use Apache Commons FileUpload, you need to have at least the following files in your webapp's /WEB-INF/lib
:
Your initial attempt failed most likely because you forgot the commons IO.
Here's a kickoff example how the doPost()
of your UploadServlet
may look like when using Apache Commons FileUpload:
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
List<FileItem> items = new ServletFileUpload(new DiskFileItemFactory()).parseRequest(request);
for (FileItem item : items) {
if (item.isFormField()) {
// Process regular form field (input type="text|radio|checkbox|etc", select, etc).
String fieldName = item.getFieldName();
String fieldValue = item.getString();
// ... (do your job here)
} else {
// Process form file field (input type="file").
String fieldName = item.getFieldName();
String fileName = FilenameUtils.getName(item.getName());
InputStream fileContent = item.getInputStream();
// ... (do your job here)
}
}
} catch (FileUploadException e) {
throw new ServletException("Cannot parse multipart request.", e);
}
// ...
}
It's very important that you don't call getParameter()
, getParameterMap()
, getParameterValues()
, getInputStream()
, getReader()
, etc on the same request beforehand. Otherwise the servlet container will read and parse the request body and thus Apache Commons FileUpload will get an empty request body. See also a.o. ServletFileUpload#parseRequest(request) returns an empty list.
Note the FilenameUtils#getName()
. This is a MSIE fix as to obtaining the file name. This browser incorrectly sends the full file path along the name instead of only the file name.
Alternatively you can also wrap this all in a Filter
which parses it all automagically and put the stuff back in the parametermap of the request so that you can continue using request.getParameter()
the usual way and retrieve the uploaded file by request.getAttribute()
. You can find an example in this blog article.
Workaround for GlassFish3 bug of getParameter()
still returning null
Note that Glassfish versions older than 3.1.2 had a bug wherein the getParameter()
still returns null
. If you are targeting such a container and can't upgrade it, then you need to extract the value from getPart()
with help of this utility method:
private static String getValue(Part part) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(part.getInputStream(), "UTF-8"));
StringBuilder value = new StringBuilder();
char[] buffer = new char[1024];
for (int length = 0; (length = reader.read(buffer)) > 0;) {
value.append(buffer, 0, length);
}
return value.toString();
}
String description = getValue(request.getPart("description")); // Retrieves <input type="text" name="description">
Saving uploaded file (don't use getRealPath()
nor part.write()
!)
Head to the following answers for detail on properly saving the obtained InputStream
(the fileContent
variable as shown in the above code snippets) to disk or database:
- Recommended way to save uploaded files in a servlet application
- How to upload an image and save it in database?
- How to convert Part to Blob, so I can store it in MySQL?
Serving uploaded file
Head to the following answers for detail on properly serving the saved file from disk or database back to the client:
- Load images from outside of webapps / webcontext / deploy folder using <h:graphicImage> or <img> tag
- How to retrieve and display images from a database in a JSP page?
- Simplest way to serve static data from outside the application server in a Java web application
- Abstract template for static resource servlet supporting HTTP caching
Ajaxifying the form
Head to the following answers how to upload using Ajax (and jQuery). Do note that the servlet code to collect the form data does not need to be changed for this! Only the way how you respond may be changed, but this is rather trivial (i.e. instead of forwarding to JSP, just print some JSON or XML or even plain text depending on whatever the script responsible for the Ajax call is expecting).
- How to upload files to server using JSP/Servlet and Ajax?
- sending a file as multipart through xmlHttpRequest
- HTML5 File Upload to Java Servlet
Hope this all helps :)
这篇关于如何使用JSP / Servlet将文件上传到服务器?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!