Java中的文件上传(原始Servlet实现)
从原始的Servlet来实现文件的上传,代码如下:
参考:https://my.oschina.net/Barudisshu/blog/150026
采用的是Multipart/form-data的方式上传文件。针对Multipart/form-data方式的上传解释,参考如下文件:
http://www.onmpw.com/tm/xwzj/network_35.html
http://892848153.iteye.com/blog/1847467
http://blog.csdn.net/five3/article/details/7181521
下面为具体的实现方式:
1、通过getInputStream()取得上传文件。
注意:这种方式相当的原始,通过分析body中的字符,然后再进行硬编码切割出文件字节,再进行保存。
JSP:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <title></title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> </head> <body> <div> <form action="UploadServlet" method="POST" enctype="multipart/form-data"> <table> <tr> <td><label for="file1">文件1:</label></td> <td><input type="file" id="file1" name="file"></td> </tr> <tr> <td><label for="file2">文件2:</label></td> <td><input type="file" id="file2" name="file"></td> </tr> <tr> <td><label for="file3">文件3:</label></td> <td><input type="file" id="file3" name="file"></td> </tr> <tr> <td><label for="file3">Text:</label></td> <td><input type="text" id="text1" name="text1"></td> </tr> <tr> <td colspan="2"><input type="submit" value="上传" name="upload"></td> </tr> </table> </form> </div> </body> </html>
Servlet:
提示:使用了servlet3.0的标注免配置功能。
package uploadtest; import java.io.DataInputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * Servlet implementation class UploadServlet */ @WebServlet("/UploadServlet") public class UploadServlet extends HttpServlet { private static final long serialVersionUID = 1L; /** * @see HttpServlet#HttpServlet() */ public UploadServlet() { super(); // TODO Auto-generated constructor stub } /** * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub } /** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub this.processRequest(request, response); } //通过getInputStream()取得上传文件。循环多文件 /** * Processes requests for both HTTP * <code>POST</code> methods. * * @param request servlet request * @param response servlet response * @throws ServletException if a servlet-specific error occurs * @throws IOException if an I/O error occurs */ protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); //读取请求Body byte[] body = readBody(request); //取得所有Body内容的字符串表示 String textBody = new String(body, "ISO-8859-1"); //取得文件区段边界信息,并通过边界循环获取文件流 String contentType = request.getContentType(); String boundaryText = String.format("--%1$s", contentType.substring(contentType.lastIndexOf("=") + 1, contentType.length())); for(String tempTextBody : textBody.split(boundaryText)){//多文件循环 if(tempTextBody.length()>0){ //取得上传的文件名称 String fileName = getFileName(tempTextBody); if(fileName.length()>0){ //取得文件开始与结束位置 Position p = getFilePosition(tempTextBody); //输出至文件 writeTo(fileName, tempTextBody.getBytes("ISO-8859-1"), p); } } } } /** * 文件起始位置类 * */ class Position { int begin; int end; public Position(int begin, int end) { this.begin = begin; this.end = end; } } /** * 获取request的getInputStream,返回byte * * @param request * @return * @throws IOException */ private byte[] readBody(HttpServletRequest request) throws IOException { //获取请求文本字节长度 int formDataLength = request.getContentLength(); //取得ServletInputStream输入流对象 DataInputStream dataStream = new DataInputStream(request.getInputStream()); byte body[] = new byte[formDataLength]; int totalBytes = 0; while (totalBytes < formDataLength) { int bytes = dataStream.read(body, totalBytes, formDataLength); totalBytes += bytes; } return body; } /** * 获取文件起始位置 * * @param request * @param textBody * @return * @throws IOException */ private Position getFilePosition(String textBody) throws IOException { //取得实际上传文件的起始与结束位置 int pos = textBody.indexOf("filename=\""); pos = textBody.indexOf("\n", pos) + 1; pos = textBody.indexOf("\n", pos) + 1; pos = textBody.indexOf("\n", pos) + 1; int begin = ((textBody.substring(0, pos)).getBytes("ISO-8859-1")).length; int end = textBody.getBytes("ISO-8859-1").length; return new Position(begin, end); } /** * 获取文件名 * * @param requestBody * @return */ private String getFileName(String requestBody) { try { String fileName = requestBody.substring(requestBody.indexOf("filename=\"") + 10); fileName = fileName.substring(0, fileName.indexOf("\n")); fileName = fileName.substring(fileName.indexOf("\n") + 1,fileName.indexOf("\"")); // 取扩展名加随机数进行重命名 fileName = new SimpleDateFormat("yyyyMMddHHmmsssss").format(new Date())+java.util.UUID.randomUUID() + fileName.substring(fileName.lastIndexOf("."),fileName.length()); return fileName; } catch (Exception e) { return ""; } } /** * 写文件 * * @param fileName * @param body * @param p * @throws IOException */ private void writeTo(String fileName, byte[] body, Position p) throws IOException { String filePath = this.getServletContext().getRealPath("")+"/Uploads/"+new SimpleDateFormat("yyyyMMdd").format(new Date())+"/";//初始化保存的位置 Tools.isExistDir(filePath);//看目录是否已经创建 FileOutputStream fileOutputStream = new FileOutputStream(filePath + fileName); fileOutputStream.write(body, p.begin, (p.end - p.begin)); fileOutputStream.flush(); fileOutputStream.close(); } }
测试工程:https://github.com/easonjim/5_java_example/tree/master/uploadtest/test1
2、通过getPart()、getParts()取得上传文件。
Servlet3.0中新增了getPart()和getParts()函数用来处理上传文件,getPart()用于上传单文件,getParts()用于上传多个文件。详细参考:http://blog.csdn.net/new_one_object/article/details/51373802
同样的,用此方法只支持multipart/form-data请求类型的文件上传。
还有一点,在Servlet上必须标注特性标记头@MultipartConfig,以表示是multipart/form-data类型的MIME。
JSP:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <title></title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> </head> <body> <div> <form action="UploadServlet" method="POST" enctype="multipart/form-data"> <table> <tr> <td><label for="file1">文件1:</label></td> <td><input type="file" id="file1" name="file1"></td> </tr> <tr> <td><label for="file2">文件2:</label></td> <td><input type="file" id="file2" name="file2"></td> </tr> <tr> <td><label for="file3">文件3:</label></td> <td><input type="file" id="file3" name="file3"></td> </tr> <tr> <td><label for="file3">Text:</label></td> <td><input type="text" id="text1" name="text1"></td> </tr> <tr> <td colspan="2"><input type="submit" value="上传" name="upload"></td> </tr> </table> </form> </div> </body> </html>
Servlet:
提示:使用了servlet3.0的标注免配置功能。
package uploadtest; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.text.SimpleDateFormat; import java.util.Date; import javax.servlet.ServletException; import javax.servlet.annotation.MultipartConfig; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.Part; /** * Servlet implementation class UploadServlet */ @MultipartConfig @WebServlet("/UploadServlet") public class UploadServlet extends HttpServlet { private static final long serialVersionUID = 1L; /** * @see HttpServlet#HttpServlet() */ public UploadServlet() { super(); // TODO Auto-generated constructor stub } /** * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub } /** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub this.processRequest(request, response); } //通过getPart()、getParts()取得上传文件。单文件 /** * Processes requests for both HTTP * <code>POST</code> methods. * * @param request servlet request * @param response servlet response * @throws ServletException if a servlet-specific error occurs * @throws IOException if an I/O error occurs */ protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Part part = request.getPart("file1");//参数就是input标签的name属性,且这个name必须是唯一的 String fileName = getFileName(part); writeTo(fileName, part); } //取得上传文件名 private String getFileName(Part part) { String header = part.getHeader("Content-Disposition"); String fileName = header.substring(header.indexOf("filename=\"") + 10, header.lastIndexOf("\"")); // 取扩展名加随机数进行重命名 fileName = new SimpleDateFormat("yyyyMMddHHmmsssss").format(new Date())+java.util.UUID.randomUUID() + fileName.substring(fileName.lastIndexOf("."),fileName.length()); return fileName; } //存储文件 private void writeTo(String fileName, Part part) throws IOException, FileNotFoundException { InputStream in = part.getInputStream(); String filePath = this.getServletContext().getRealPath("")+"/Uploads/"+new SimpleDateFormat("yyyyMMdd").format(new Date())+"/";//初始化保存的位置 Tools.isExistDir(filePath);//看目录是否已经创建 OutputStream out = new FileOutputStream(filePath + fileName); byte[] buffer = new byte[1024]; int length = -1; while ((length = in.read(buffer)) != -1) { out.write(buffer, 0, length); } in.close(); out.close(); } }
通过上面的代码很明显的区别出这个方法简单明了,省去了切割字符串的问题。
接下来再升级简化一下流的写入,将用到@MultipartConfig特性中的location属性,这个属性将指定一个本地目录,然后调用wtite方法直接写入。不过这个也有一个不好的特点,路径是死的,没法按照自定义输出。
Servlet改造如下:
package uploadtest; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.text.SimpleDateFormat; import java.util.Date; import javax.servlet.ServletException; import javax.servlet.annotation.MultipartConfig; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.Part; /** * Servlet implementation class UploadServlet */ @MultipartConfig(location = "d:\\\\workspace") @WebServlet("/UploadServlet3") public class UploadServlet3 extends HttpServlet { private static final long serialVersionUID = 1L; /** * @see HttpServlet#HttpServlet() */ public UploadServlet3() { super(); // TODO Auto-generated constructor stub } /** * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub } /** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub this.processRequest(request, response); } //通过getPart()、getParts()取得上传文件。单文件 /** * Processes requests for both HTTP * <code>POST</code> methods. * * @param request servlet request * @param response servlet response * @throws ServletException if a servlet-specific error occurs * @throws IOException if an I/O error occurs */ protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Part part = request.getPart("file1");//参数就是input标签的name属性,且这个name必须是唯一的 String fileName = getFileName(part); //将文件写入location指定的目录 part.write(fileName); } //取得上传文件名 private String getFileName(Part part) { String header = part.getHeader("Content-Disposition"); String fileName = header.substring(header.indexOf("filename=\"") + 10, header.lastIndexOf("\"")); // 取扩展名加随机数进行重命名 fileName = new SimpleDateFormat("yyyyMMddHHmmsssss").format(new Date())+java.util.UUID.randomUUID() + fileName.substring(fileName.lastIndexOf("."),fileName.length()); return fileName; } }
对于@MultipartConfig更多的解释,参考:http://blog.csdn.net/chenqipc/article/details/50551450
测试工程(包含了多文件的实现):https://github.com/easonjim/5_java_example/tree/master/uploadtest/test2