Java中的文件上传(原始Servlet实现)

时间:2023-03-09 19:56:26
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

相关文章