JSP上传文件
背景:
3月,公司的实习生来了,每年都会招,招来之后人家看明白你公司环境后,毕业就和你拜拜了。不管怎么样,部门要求培训、要求每天写日报,这下可好,我们组有5个实习生,每个5个Excel,天天如此,而且Excel每个1M,我就得天天删来删去,不仅仅我们小组的,别的小组也这样,于是,我就想能不能部署个傻瓜应用,每天你们上传上去,我去下载,然后我在上传上去。呵呵,这个想法比较幼稚啊。。。。。。。。。。对于开发Web的各位来说。
我Web的功力实在太差,于是正好前一阵子写了一个web小程序,一直处于开发进展中,我就加个小功能吧,先把原型搭出来。上网查了一下: 如果想要上传的话必须把form的enctype的格式设置成“multipart/form-data”,注意默认是什么URLEncoded的,这个咱也就不关心了。设置完之后,还必须是Post请求,必须的吗,上传文件还能Get?另外Servlet和JSP规范中并没有对这种格式进行解析,因此还得额外自己写代码。我shit,你下载文件只要设置一下响应头,你规范就不能为上传文件多做点东西么,还要我自己额外解析,虽说我对Web了解不多,可Apache我知道,common-fileupload这个组件我可知道,一查API,果真可以处理,等等,既然做了,就先自己造个轮子,毕竟是自己学习又不是做项目;再在网上搜,不错,找到oreilly上的一个开源Servlet工具库,看起来评价不错,我先收藏,慢慢再看。
对付这种web流,我就两种处理方式:
1、用HTTPWatch或者类似软件截获HTTP流,分析客户端提交数据
2、调试阶段在Web应用服务器上打出响应流信息,先别考虑神马性能问题。。。那是后话。
1、2弄完了,好了发现格式了如下,最好对着RFC看,但是真心看RFC太烂费时间了,不过对于真正想要做好这种类型的事情不仔细研读RFC以及各种浏览器的行为,总是会出现这样或者那样的问题。
Content-Disposition: form-data; name="upfile"; filename="C:\Documents and Settings\Administrator\桌面\小舅好啊.doc"
Content-Type: application/msword
通过分析流,发现其实邮件传输和这个大同小异,除了邮件使用了Base64编码吧?印象中好象是。
在文件流结尾会有这个:
-------------------------------7dc1e411c05fe
因此,通过分析boundary确定边界、通过分析Content-Disposition获取文件元信息,当然了,也可以不传文件,这样仅仅什么也不做而已。
分析完了,奉上我第一版本的粗糙代码,说实话,我这个就是为了研究原理,里面代码存在很大问题,先说出来,别让大家喷我:
1、编码格式,编码格式很重要,文件名可能是中文,因此需要设置UTF-8或者GBK,但是在解析流的时候,Encoding只能在解析元数据信息的时候的使用,其余时候
各位还是安心使用ASCII就是字节流吧,这样原来文件是什么,存储的就是什么,肯定不会出现问题,之前我用GBK了,就带来Excel上传每次都有格式问题。
2、流处理问题,缓冲流的操作,并且读取流需要分阶段来处理,先处理元数据信息,获取完后在处理文件真实信息,避免过多用户上传导致程序繁忙。
JSP如下,一个简单的JSP
pageEncoding="gb2312"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>SOC FileUpload</title>
</head>
<body>
<!-- <%=application.getServerInfo()%><br>-->
<form method="post" action="/soc/UploadFile"
enctype="multipart/form-data">
请选择要上传的文件<input type="file" name="upfile" size="40"><input
type="submit" value="提交">
</form>
</body>
</html>
Servlet:
private static final long serialVersionUID = 1L;
// 10M
// 这个版本的代码性能问题,读取大数据需要
// 逐步读取
public static final long MAX_SIZE = 1024 * 1024 * 10;
static final String FILE_NAME = "filename=\"";
static final String BOUNDRAY = "boundary=";
/**
* @see HttpServlet#HttpServlet()
*/
public UploadFile() {
super();
LogUtil.debug("UploadFile load.");
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
// request set encoding
LogUtil.debug("UploadFile called.");
LogUtil.debug(request.getServletPath());
LogUtil.debug(getServletContext().getRealPath(request.getServerName()));
String base = (getServletContext().getRealPath(request.getServerName()));
// platform
base = base.substring(0, base.lastIndexOf(File.separatorChar) + 1);
String rootPath = base + "upload";
String contentType = request.getContentType();
LogUtil.debug(contentType);
LogUtil.debug("donwload store: " + rootPath);
if (contentType.indexOf("multipart/form-data") >= 0) {
int length = request.getContentLength();
if (length > MAX_SIZE) {
response.getWriter().println(
"<P> File can't exceed " + MAX_SIZE + "<P><br>");
return;
}
byte[] total = new byte[length];
int totalRead = 0;
int byteRead = 0;
// not very good
while (totalRead < length) {
byteRead = request.getInputStream().read(total, totalRead,
length - totalRead);
totalRead += byteRead;
}
String raw = new String(total);
// LogUtil.debug("Raw: " + raw);
LogUtil.debug("total: " + length + " read: " + totalRead);
String saveFile = raw.substring(raw.indexOf(FILE_NAME)
+ FILE_NAME.length());
saveFile = saveFile.substring(0, saveFile.indexOf("\n"));
saveFile = saveFile.substring(0, saveFile.lastIndexOf("\""));
// 判断浏览器
saveFile = saveFile.substring(saveFile.lastIndexOf("\\") + 1);
LogUtil.debug("File: " + saveFile);
String boudary = contentType
.substring(contentType.lastIndexOf("=") + 1);
LogUtil.debug("Bound: " + boudary);
String destFileName = rootPath + File.separatorChar + saveFile;
LogUtil.debug(destFileName);
int pos = raw.indexOf(FILE_NAME);
pos = raw.indexOf("\n", pos) + 1;
pos = raw.indexOf("\n", pos) + 1;
pos = raw.indexOf("\n", pos) + 1;
int startPosition = raw.substring(0, pos).getBytes().length;
// -4
LogUtil.debug("Start: " + raw.substring(0, pos));
raw = null;
raw = new String(total, "ascii");
int boundaryPosition = raw.indexOf(boudary, pos) - 4;
// LogUtil.debug("Bound: " + raw.substring(0, boundaryPosition));
int endPosition = raw.substring(0, boundaryPosition).getBytes(
"ascii").length;
// -----------------------------7dc251175d904aa
// -----------------------------7dc251175d904aa--
// ---------------------------7dc251175d904aa
File file = new File(destFileName);
if (!file.exists()) {
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
file.createNewFile();
}
FileOutputStream fout = new FileOutputStream(file);
int destNumber = endPosition - startPosition;
LogUtil.debug("Start: " + startPosition + " Boundary: "
+ boundaryPosition + " End:" + endPosition + " Number: "
+ destNumber);
// LogUtil.debug("DestLog: "
// + new String(total, startPosition, destNumber));
// 避免写大文件
ByteArrayInputStream bin = new ByteArrayInputStream(total,
startPosition, destNumber);
byte[] cache = new byte[8 * 1024];
int totalWrite = 0;
while (totalWrite < destNumber) {
int cnt = bin.read(cache);
fout.write(cache, 0, cnt);
fout.flush();
totalWrite += cnt;
}
System.out.println("Write Total: " + totalWrite);
fout.close();
response.getWriter().println("<P>Upload File Successful<P><br>");
response.getWriter().flush();
}
}
}
弄完后,我就知道,我这个处理不了多文件,其实也能处理,但是各位看官也看见了,代码写的实在难看,我连重构的欲望都没有,看来写程序必须先思考一下啊。。。奉上一个Oreilly的例子,看看这个多简洁:
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public UploadFileEx() {
super();
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
// http://www.javaworld.com.tw/confluence/pages/viewpage.action?pageId=692
int maxPostSize = 5 * 1024 * 1024;
MultipartRequest multi = new MultipartRequest(request, "d:\\",
maxPostSize, "GB2312");
Enumeration<?> filenames = multi.getFileNames();
while (filenames.hasMoreElements()) {
String filename = (String) filenames.nextElement();
System.out.println("Upload -> " + filename);
}
}
}
短短几行,功能完成,当然Jar包里面也很多类处理了。核心是封装一个MultipartRequest ,这个类里面调用MultipartParser生成不同的Part,不同的Part有不同的
处理方法。
附带两个URL链接:
http://commons.apache.org/fileupload/
希望对大家有所帮助。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix