宛若重生
2014-04-26 13:22:05
java servlet实现上传文件代码及其原理说明
我们做文件上传是不是都是使用第三方插件,虽然知道原理,但想知道是怎么实现的吧?一起来看看
上传解析的实现简单说一下:
通过ServletRequest类的getInputStream()方法获得一个客户端向服务器发出的数据流、分析上传的文件格式,根据分析结果将多个文件依次输出服务器端的目标文件中。
格式类似下面:
//文件分隔符 -----------------------------7d226137250336 //文件信息头 Content-Disposition: form-data; name="FILE1"; filename="C:\Documents and Settings\Administrator.TIMBER-4O6B0ZZ0\My Documents\tt.sql" Content-Type: text/plain //源文件内容 create table info( content image null); //下一个文件的分隔符 -----------------------------7d226137250336 Content-Disposition: form-data; name="FILE2"; filename="" Content-Type: application/octet-stream -----------------------------7d226137250336
每个表单提交的元素都有分隔符将其分隔,其提交的表单元素的名称和对应的输入值之间也有特殊的字符将其分隔开。
参照了pell中的MultipartRequest类写了一个上传组件,代码如下:
/* * 只支持在windows下上传文件 */ package study.http.upload; import java.io.BufferedInputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import javax.servlet.ServletException; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class TestServlet extends HttpServlet { public final static String DEFAULT_ENCODING = "ISO8859_1"; public final static String CHINESE_ENCODING = "GBK"; public final static String SIGN_BOUNDARY = "boundary="; public final static String SIGN_FORMELEMENT = "name="; public final static String SIGN_FORMFILE = "filename="; public final static String SIGN_NOTFILE = "application/octet-stream"; public final static String SIGN_MULTIDATA = "multipart/form-data"; public final static String CHINESE_CONTENTTYPE = "text/html; charset=GBK"; private Hashtable paratable = new Hashtable(); private Hashtable filetable = new Hashtable(); private String strBoundary = ""; private String strSavePath=""; private static void println(String s) { System.out.println(s); } /** * 增加数据到对应的Hashtable中 * 说明:如果Hashtable中已存在该键值,则将新增加的和原来的都封装到列表中。 */ private static void addElement(Hashtable table, String paraName, Object paraValue) { ArrayList list = new ArrayList(); if (table.containsKey(paraName)) { Object o = table.get(paraName); if (o instanceof List) { ((List) o).add(paraValue); } else { list.add(o); list.add(paraValue); o = list; } table.put(paraName, o); } else { table.put(paraName, paraValue); } } public static String getHashInfo(Hashtable paratable){ StringBuffer sb=new StringBuffer(); Set keySet=paratable.keySet(); Iterator it=keySet.iterator(); while(it.hasNext()){ Object keyobj=it.next(); Object valueobj=paratable.get(keyobj); sb.append("<tr>"); sb.append("<td>"+keyobj.toString()+"</td>"); if(valueobj instanceof List){ sb.append("<td>"); int isize=((List)valueobj).size(); for(int i=0;i<isize;i++){ Object tempobj=((List)valueobj).get(i); if(i<isize-1){ sb.append(tempobj.toString()+","); } else{ sb.append(tempobj.toString()); } } sb.append("</td>"); } else{ sb.append("<td>"+valueobj.toString()+"</td>"); } sb.append("</tr>"); } return sb.toString(); } private static byte[] getfileBytes(InputStream is) { List byteList = new ArrayList(); byte[] filebyte = null; int readbyte = 0; try { while ((readbyte = is.read()) != -1) { byteList.add(new Byte((byte) readbyte)); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } filebyte = new byte[byteList.size()]; for (int i = 0; i < byteList.size(); i++) { filebyte[i] = ((Byte) byteList.get(i)).byteValue(); } return filebyte; } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { paratable = new Hashtable(); filetable = new Hashtable(); strSavePath=this.getInitParameter("savepath"); File file=new File(strSavePath); if(!file.exists()){ file.mkdirs(); } String contentType = request.getContentType(); strBoundary = getBoundary(contentType); ServletInputStream sis = request.getInputStream(); BufferedInputStream bis = new BufferedInputStream(sis); parseInputStream(bis); appendPara(request.getParameterMap()); /*追加url对应传递的参数*/ response.setContentType(CHINESE_CONTENTTYPE); // response.getWriter().write(getOutPutInfo()); // response.getWriter().write(new String(getfileBytes(sis),"GBK")); bis.close(); sis.close(); request.setAttribute("para",paratable); request.setAttribute("file",filetable); this.getServletContext().getRequestDispatcher("/result.jsp"). forward(request,response); } /** * 不用Hashtable对应的put方法,目的避免覆盖重复的键值 */ private void appendPara(Map map){ if(map!=null){ Set keySet=map.keySet(); Iterator it=keySet.iterator(); while(it.hasNext()){ Object keyobj=it.next(); String[] valueobj=(String[])map.get(keyobj); println("keyobj===="+keyobj); println("valueobj===="+valueobj); for(int i=0;i<valueobj.length;i++){ addElement(paratable,(String)keyobj,valueobj[i]); } } } } /** * 输出上传表单信息 */ protected String getOutPutInfo() { StringBuffer sb = new StringBuffer(); sb.append("<table width=100% border=1>"); sb.append("<tr><td>参数名</td><td>参数值</td></tr>"); sb.append(getHashInfo(paratable)); sb.append(getHashInfo(filetable)); sb.append("</table>"); return sb.toString(); } /** * 解析字节流 */ private void parseInputStream(InputStream is) { byte[] sizes = getfileBytes(is); int icount = 0; String s = ""; int readbyte = 0; String reals; try { reals = new String(sizes, DEFAULT_ENCODING); String realsvalue = new String(sizes, CHINESE_ENCODING); String[] arrs = reals.split(strBoundary); String[] arrsvalue = realsvalue.split(strBoundary); for (int i = 0; i < arrs.length; i++) { String tempStr = arrs[i]; String tempStr2 = arrsvalue[i]; if (tempStr.indexOf(SIGN_FORMFILE) >= 0) { readFile(tempStr, tempStr2); } else { readParameter(tempStr2); } } } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } /** * 获取本次上传对应的表单元素间的分隔符,注意该分隔符是随机生成的 */ private String getBoundary(String contentType) { String tempStr = ""; if (contentType != null && contentType.startsWith(SIGN_MULTIDATA) && contentType.indexOf(SIGN_BOUNDARY) != -1) { //获取表单每个元素的分隔符 tempStr = contentType .substring( contentType.indexOf(SIGN_BOUNDARY) + SIGN_BOUNDARY.length()).trim(); } return tempStr; } /** * 解析文件上传对应的字节流。实现算法<br> * 通过解析ISO8859_1编码方式的字符串后转换成对应上传文件的字节。 * 通过解析GBK编码方式的字符串后转换成对应上传文件的文件名。 * 说明:因不清楚字节在不同编码方式下的关系,只好使用两个字符串(比较影响性能,以后优化) * @param s 以ISO8859_1编码方式组成的字符串 * @param s2 以GBK编码方式组成的字符串 */ private void readFile(String s, String s2) { int filepos = -1; if ((filepos = s.indexOf(SIGN_FORMFILE)) >= 0) { String realName = readFileName(s2); //部分确定上传的是文件而不是任意输入的字符串 if(!realName.equals("")&& realName.length()>0 && (realName.indexOf(".")>=0)){ String filepath = readWriteFile(s, realName); addElement(filetable, realName, filepath); } } else { /*上传的不是文件*/ if (s.indexOf(SIGN_NOTFILE) >= 0) { return; } } } /** * 解析文件上传对应的名称 * 实现说明:如果上传的是文件对应格式为:<br>filename="文件名"</br> 格式 * 通过处理可以拆分出对应的文件名 * @param s 以GBK编码方式组成的包含文件名的字符串 * @return 对应上传文件的文件名(不包括文件路径) */ private String readFileName(String s) { int filepos = s.indexOf(SIGN_FORMFILE); String tempstr = s.substring(filepos + SIGN_FORMFILE.length() + 1); int iendpos = tempstr.indexOf("\""); String fileName = tempstr.substring(0, iendpos); int ifilenamepos = fileName.lastIndexOf("\\"); String realName = fileName.substring(ifilenamepos + 1); return realName; } /** * 通过解析ISO8859_1编码方式的字符串后转换成对应上传文件的字节。 * 实现算法说明:文件名转化后的字节和具体的文件字节中间是以两个重复的两个字符隔开, * 对应char值为13,10,转换后的字符对应的最后四个字符也是格式字符,获取对应中间的字节即为 * 上传文件的真正的字节数 * @param s 以ISO8859_1编码方式组成的包含文件名和具体文件字节的字符串 * @param realName 对应的文件名 * @return 对应生成的文件名包括全路径 */ private String readWriteFile(String s, String realName) { int filepos = s.indexOf(SIGN_FORMFILE); String tempstr = s.substring(filepos + SIGN_FORMFILE.length() + 1); int icount = 0; while (true) { int charnum = tempstr.charAt(icount); int charnum2 = tempstr.charAt(icount + 1); int charnum3 = tempstr.charAt(icount + 2); int charnum4 = tempstr.charAt(icount + 3); if (charnum == 13 && charnum2 == 10 && charnum3 == 13 && charnum4 == 10) { break; } icount++; } String filevalue = tempstr.substring(icount + 4, tempstr.length() - 4); FileOutputStream fos = null; String createName=strSavePath + realName; File uploadfile = new File(createName); String shortname=realName.substring(0,realName.lastIndexOf(".")); String filetype=realName.substring(realName.lastIndexOf(".")+1); int namecount=1; while(uploadfile.exists()){ createName=strSavePath+shortname+"["+namecount+"]"+"."+filetype; uploadfile=new File(createName); namecount++; } try { byte[] filebytes = filevalue.getBytes(DEFAULT_ENCODING); fos = new FileOutputStream(uploadfile); fos.write(filebytes); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e1) { e1.printStackTrace(); } finally { try { fos.close(); } catch (IOException e2) { e2.printStackTrace(); } } return createName; } /** * 解析提交过来的表单元素对应的名称以及值<br> * 实现说明:如果表单元素的是对应格式为:<br>name="表单元素名"</br> 格式 * 表单元素名和具体的输入值中间是以两个重复的两个字符隔开, * 对应char值为13,10,转换后的字符对应的最后四个字符也是格式字符,获取对应中间的字符即为 * 表单元素的输入值 * 通过处理可以拆分出对应的表单元素名以及输入值 * @param s 以GBK编码方式组成的包含表单元素名和值的字符串 */ private void readParameter(String s) { String paraName = ""; String paraValue = ""; int istartlen = -1; int iendlen = -1; if ((istartlen = s.indexOf(SIGN_FORMELEMENT)) >= 0) { String tempstr = s.substring(istartlen + SIGN_FORMELEMENT.length()+ 1); int nameindex = tempstr.indexOf("\""); paraName = tempstr.substring(0, nameindex); paraValue = tempstr.substring(nameindex + 5, tempstr.length() - 4); addElement(paratable, paraName, paraValue); } } }
组件简单说明:
上传路径在servlet初始参数中设定。
上传的表单元素、文件数据分别封装在Hashtable中。
做了测试,测试环境说明:
AppServer: WeblogicSP4
OS: WindowXP/ Soloaris 9.0
测试程序:
index.jsp(文件上传页面):
<html> <head><title>File Upload</title></head> <body> <form name="kkkkkk" action="test.upload?ssss=bbbbbbbbb&ccccc=eeeeeeee" enctype="multipart/form-data" method="post" > <input type=text name="ssss" ><br> <input type=text name="ssss" ><br> <input type=text name="ssss3" ><br> <textarea name="araea"></textarea><br> <input type=file name="cccc" ><br> <input type=file name="ddddd" ><br> <input type=submit value="submit" name="bbbbbbbbb"> </form> </body> </html>
result.jsp(查看提交表单数据)
<%@ page contentType="text/html;charset=GBK"%> <%@ page import="java.util.*"%> <%@ page import="study.http.upload.*"%> <% Hashtable paratable=(Hashtable)request.getAttribute("para"); Hashtable filetable=(Hashtable)request.getAttribute("file"); String parastr=TestServlet.getHashInfo(paratable); out.println("<table width=100% border=1>"); out.println(parastr); out.println(TestServlet.getHashInfo(filetable)); out.println("</table>"); %> <html> <head><title>File Upload</title></head> <body> </body> </html>
测试时对应的web应用已经指定相关字符集,weblogic.xml内容如下:
<weblogic-web-app> <charset-params> <input-charset> <resource-path>/*</resource-path> <java-charset-name>GBK</java-charset-name> </input-charset> </charset-params> </weblogic-web-app>
原文地址:http://www.blogjava.net/beauty_beast/archive/2005/10/13/15455.html
由最代码官方编辑于2016-6-4 10:14:28
猜你喜欢
- java servlet上传文件可判断文件类型、大小、支持批量的脚本代码
- GodSon Easyui结合Pluplaod插件通过java servlet上传文件演示demo源代码下载
- java servlet无刷新上传图片文件并裁剪demo代码(Jcrop)
- jsp/servlet 表单上传图片、但不能传值了、或传值中文乱码问题解决
- java web servlet文件上传到服务器源代码下载
- java servlet通过SmartUpload开发文件批量上传、批量下载源码分享
- easyui结合plupload插件通过java servlet实现上传文件示例
- java servlet实现文件的上传功能
- 服务器启动servlet时删除以xls结尾的文件
- java Servlet上传下载文件http协议原理详解
- 基于servlet和jsp的文件上传下载小例子
- JQuery和Servlet实现文件上传进度条,能显示上传速度,上传的百分比等
请下载代码后再发表评论
相关代码
- jsp/servlet 表单上传图片、但不能传值了、或传值中文乱码问题解决
- java servlet开发购物车功能,实现增删改查结算等功能。
- 基于java Servlet的验证码生成代码
- 用servlet写的JPetStore,实现了全部基本功能
- 原 java web servlet文件上传到服务器源代码下载
- 原证精 java servlet对html网页压缩后返回的过滤器代码实现
- java servlet写的一个可以显示用户的分数,有源代码和数据库
- 证 java Servlet生成验证码图片工具类代码
- java servlet Filter的入门实例源代码下载
- 原 java servlet集成润乾报表实例
- java servlet开发CAS单点登录Demo
- java servlet获取客户端请求ip地址
最近下载
最近浏览
哪里的完整版 LV7
9月3日
磊哥哥哥哥 LV13
2023年12月26日
uni-code_0123 LV1
2023年11月11日
jiemomo LV12
2023年10月19日
110111 LV2
2023年6月3日
人工智能4708 LV11
2023年5月1日
a197304a
2023年1月8日
暂无贡献等级
Java开发工程师_初心 LV1
2022年12月17日
Dominick LV14
2022年11月17日
微信网友_6190641661054976 LV2
2022年10月27日