采用HTTP协议上传文件实现(java)

通过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类写了一个上传组件(本来不想自己写的,想改造改造就完事的,可惜反编译出来的代码比较难读),代码如下:

  1/**//*
  2 * 只支持在windows下上传文件
  3 * Created on 2005-10-10
  4 *
  5 * TODO To change the template for this generated file go to
  6 * Window - Preferences - Java - Code Style - Code Templates
  7 */
  8package study.http.upload;
  9
10import java.io.BufferedInputStream;
11import java.io.File;
12import java.io.FileNotFoundException;
13import java.io.FileOutputStream;
14import java.io.IOException;
15import java.io.InputStream;
16import java.io.UnsupportedEncodingException;
17import java.util.ArrayList;
18import java.util.Hashtable;
19import java.util.Iterator;
20import java.util.List;
21import java.util.Map;
22import java.util.Set;
23
24import javax.servlet.ServletException;
25import javax.servlet.ServletInputStream;
26import javax.servlet.http.HttpServlet;
27import javax.servlet.http.HttpServletRequest;
28import javax.servlet.http.HttpServletResponse;
29
30/** *//**
31 * @author liusuifeng
32 *
33 * TODO To change the template for this generated type comment go to Window -
34 * Preferences - Java - Code Style - Code Templates
35 */
36public class TestServlet extends HttpServlet {
37
38    public final static String DEFAULT_ENCODING = "ISO8859_1";
39
40    public final static String CHINESE_ENCODING = "GBK";
41
42    public final static String SIGN_BOUNDARY = "boundary=";
43
44    public final static String SIGN_FORMELEMENT = "name=";
45
46    public final static String SIGN_FORMFILE = "filename=";
47
48    public final static String SIGN_NOTFILE = "application/octet-stream";
49
50    public final static String SIGN_MULTIDATA = "multipart/form-data";
51
52    public final static String CHINESE_CONTENTTYPE = "text/html; charset=GBK";
53
54    private Hashtable paratable = new Hashtable();
55
56    private Hashtable filetable = new Hashtable();
57
58    private String strBoundary = "";
59   
60    private String strSavePath="";
61   
62
63    private static void println(String s) {
64        System.out.println(s);
65    }
66   
67   
68   
69
70    /** *//**
71     * 增加数据到对应的Hashtable中
72     * 说明:如果Hashtable中已存在该键值,则将新增加的和原来的都封装到列表中。
73     * @param table   
74     * @param paraName
75     * @param paraValue
76     */
77    private static void addElement(Hashtable table, String paraName,
78            Object paraValue) {
79        ArrayList list = new ArrayList();
80        if (table.containsKey(paraName)) {
81            Object o = table.get(paraName);
82            if (o instanceof List) {
83                ((List) o).add(paraValue);
84            } else {
85                list.add(o);
86                list.add(paraValue);
87                o = list;
88            }
89            table.put(paraName, o);
90        } else {
91            table.put(paraName, paraValue);
92        }
93    }
94
95    public static String getHashInfo(Hashtable paratable){
96        StringBuffer sb=new StringBuffer();
97        Set keySet=paratable.keySet();
98        Iterator it=keySet.iterator();
99        while(it.hasNext()){
100           
101            Object keyobj=it.next();
102            Object valueobj=paratable.get(keyobj);
103           
104            sb.append("<tr>");
105            sb.append("<td>"+keyobj.toString()+"</td>");
106            if(valueobj instanceof List){
107                sb.append("<td>");
108                int isize=((List)valueobj).size();
109                for(int i=0;i<isize;i++){
110                    Object tempobj=((List)valueobj).get(i);
111                    if(i<isize-1){
112                       sb.append(tempobj.toString()+",");
113                    }
114                    else{
115                       sb.append(tempobj.toString());
116                    }
117                }
118               
119                sb.append("</td>");
120            }
121            else{
122                sb.append("<td>"+valueobj.toString()+"</td>");
123            }
124            sb.append("</tr>");
125        }
126        return sb.toString();
127    }
128   
129   
130    private static byte[] getfileBytes(InputStream is) {
131        List byteList = new ArrayList();
132        byte[] filebyte = null;
133        int readbyte = 0;
134        try {
135            while ((readbyte = is.read()) != -1) {
136                byteList.add(new Byte((byte) readbyte));
137            }
138        } catch (FileNotFoundException e) {
139            e.printStackTrace();
140        } catch (IOException e) {
141            e.printStackTrace();
142        }
143        filebyte = new byte[byteList.size()];
144        for (int i = 0; i < byteList.size(); i++) {
145            filebyte[i] = ((Byte) byteList.get(i)).byteValue();
146        }
147        return filebyte;
148
149    }
150   
151   
152
153   
154    protected void doGet(HttpServletRequest request,
155            HttpServletResponse response) throws ServletException, IOException {
156        doPost(request, response);
157    }
158
159    protected void doPost(HttpServletRequest request,
160            HttpServletResponse response) throws ServletException, IOException {
161        paratable = new Hashtable();
162        filetable = new Hashtable();
163        strSavePath=this.getInitParameter("savepath");
164        File file=new File(strSavePath);
165        if(!file.exists()){
166            file.mkdirs();
167        }
168        String contentType = request.getContentType();   
169        strBoundary = getBoundary(contentType);
170        ServletInputStream sis = request.getInputStream();
171        BufferedInputStream bis = new BufferedInputStream(sis);
172        parseInputStream(bis);
173        appendPara(request.getParameterMap());  /**//*追加url对应传递的参数*/
174        response.setContentType(CHINESE_CONTENTTYPE);
175       
176//        response.getWriter().write(getOutPutInfo());
177//        response.getWriter().write(new String(getfileBytes(sis),"GBK"));
178        bis.close();
179        sis.close();
180        request.setAttribute("para",paratable);
181        request.setAttribute("file",filetable);
182       
183        this.getServletContext().getRequestDispatcher("/result.jsp").
184        forward(request,response);
185       
186    }
187   
188   
189    /** *//**
190     * 不用Hashtable对应的put方法,目的避免覆盖重复的键值
191     * @return
192     */
193    private void appendPara(Map map){
194       
195        if(map!=null){
196            Set keySet=map.keySet();
197            Iterator it=keySet.iterator();
198            while(it.hasNext()){
199                Object keyobj=it.next();
200                String[] valueobj=(String[])map.get(keyobj);
201                println("keyobj===="+keyobj);
202                println("valueobj===="+valueobj);
203                for(int i=0;i<valueobj.length;i++){
204                    addElement(paratable,(String)keyobj,valueobj[i]);
205                }
206            }
207        }
208    }
209   
210   
211
212    /** *//**
213     * 输出上传表单信息
214     *
215     * @param pw
216     */
217    protected String getOutPutInfo() {
218        StringBuffer sb = new StringBuffer();
219        sb.append("<table width=100% border=1>");
220        sb.append("<tr><td>参数名</td><td>参数值</td></tr>");
221        sb.append(getHashInfo(paratable));
222        sb.append(getHashInfo(filetable));
223        sb.append("</table>");
224        return sb.toString();
225    }
226
227    /** *//**
228     * 解析字节流
229     * @param is
230     */
231    private void parseInputStream(InputStream is) {
232        byte[] sizes = getfileBytes(is);
233        int icount = 0;
234        String s = "";
235        int readbyte = 0;
236        String reals;
237        try {
238            reals = new String(sizes, DEFAULT_ENCODING);
239            String realsvalue = new String(sizes, CHINESE_ENCODING);
240            String[] arrs = reals.split(strBoundary);
241            String[] arrsvalue = realsvalue.split(strBoundary);
242            for (int i = 0; i < arrs.length; i++) {
243                String tempStr = arrs[i];
244                String tempStr2 = arrsvalue[i];
245                if (tempStr.indexOf(SIGN_FORMFILE) >= 0) {
246                    readFile(tempStr, tempStr2);
247                } else {
248                    readParameter(tempStr2);
249                }
250            }
251        } catch (UnsupportedEncodingException e) {
252            e.printStackTrace();
253        }
254
255    }
256
257    /** *//**
258     * 获取本次上传对应的表单元素间的分隔符,注意该分隔符是随机生成的
259     * @param contentType  
260     * @return
261     */
262    private String getBoundary(String contentType) {
263        String tempStr = "";
264        if (contentType != null && contentType.startsWith(SIGN_MULTIDATA)
265                && contentType.indexOf(SIGN_BOUNDARY) != -1) {
266            //获取表单每个元素的分隔符
267            tempStr = contentType
268                    .substring(
269                            contentType.indexOf(SIGN_BOUNDARY)
270                                    + SIGN_BOUNDARY.length()).trim();
271        }
272        return tempStr;
273    }
274
275    /** *//**
276     * 解析文件上传对应的字节流。实现算法<br>
277     * 通过解析ISO8859_1编码方式的字符串后转换成对应上传文件的字节。
278     * 通过解析GBK编码方式的字符串后转换成对应上传文件的文件名。
279     * 说明:因不清楚字节在不同编码方式下的关系,只好使用两个字符串(比较影响性能,以后优化)
280     * @param s   以ISO8859_1编码方式组成的字符串
281     * @param s2  以GBK编码方式组成的字符串
282     */
283    private void readFile(String s, String s2) {
284        int filepos = -1;
285        if ((filepos = s.indexOf(SIGN_FORMFILE)) >= 0) {
286            String realName = readFileName(s2);
287            //部分确定上传的是文件而不是任意输入的字符串
288            if(!realName.equals("")&& realName.length()>0 && (realName.indexOf(".")>=0)){
289                String filepath = readWriteFile(s, realName);
290                addElement(filetable, realName, filepath);
291            }
292        }
293        else {
294            /**//*上传的不是文件*/
295            if (s.indexOf(SIGN_NOTFILE) >= 0) {
296                return;
297            }
298        }
299
300    }
301   
302    /** *//**
303     * 解析文件上传对应的名称
304     * 实现说明:如果上传的是文件对应格式为:<br>filename="文件名"</br> 格式
305     * 通过处理可以拆分出对应的文件名 
306     * @param s   以GBK编码方式组成的包含文件名的字符串
307     * @return    对应上传文件的文件名(不包括文件路径)
308     */
309    private String readFileName(String s) {
310        int filepos = s.indexOf(SIGN_FORMFILE);
311        String tempstr = s.substring(filepos + SIGN_FORMFILE.length() + 1);
312        int iendpos = tempstr.indexOf("\"");
313        String fileName = tempstr.substring(0, iendpos);
314        int ifilenamepos = fileName.lastIndexOf("\\");
315        String realName = fileName.substring(ifilenamepos + 1);       
316        return realName;
317
318    }
319
320    /** *//**
321     * 通过解析ISO8859_1编码方式的字符串后转换成对应上传文件的字节。
322     * 实现算法说明:文件名转化后的字节和具体的文件字节中间是以两个重复的两个字符隔开,
323     * 对应char值为13,10,转换后的字符对应的最后四个字符也是格式字符,获取对应中间的字节即为
324     * 上传文件的真正的字节数
325     * @param s        以ISO8859_1编码方式组成的包含文件名和具体文件字节的字符串
326     * @param realName  对应的文件名
327     * @return          对应生成的文件名包括全路径
328     */
329    private String readWriteFile(String s, String realName) {
330        int filepos = s.indexOf(SIGN_FORMFILE);
331        String tempstr = s.substring(filepos + SIGN_FORMFILE.length() + 1);
332        int icount = 0;
333        while (true) {
334            int charnum = tempstr.charAt(icount);
335            int charnum2 = tempstr.charAt(icount + 1);
336            int charnum3 = tempstr.charAt(icount + 2);
337            int charnum4 = tempstr.charAt(icount + 3);
338            if (charnum == 13 && charnum2 == 10 && charnum3 == 13
339                    && charnum4 == 10) {
340                break;
341            }
342            icount++;
343        }
344        String filevalue = tempstr.substring(icount + 4, tempstr.length() - 4);
345        FileOutputStream fos = null;
346        String createName=strSavePath + realName;
347        File uploadfile = new File(createName);       
348        String shortname=realName.substring(0,realName.lastIndexOf("."));
349        String filetype=realName.substring(realName.lastIndexOf(".")+1);
350        int namecount=1;
351        while(uploadfile.exists()){           
352            createName=strSavePath+shortname+"["+namecount+"]"+"."+filetype;
353            uploadfile=new File(createName);
354            namecount++;
355           
356        }
357        try {
358            byte[] filebytes = filevalue.getBytes(DEFAULT_ENCODING);
359            fos = new FileOutputStream(uploadfile);
360            fos.write(filebytes);
361        } catch (FileNotFoundException e) {
362            e.printStackTrace();
363        } catch (IOException e1) {
364
365            e1.printStackTrace();
366        } finally {
367            try {
368                fos.close();
369            } catch (IOException e2) {
370
371                e2.printStackTrace();
372            }
373        }
374
375        return createName;
376    }
377
378   
379    /** *//**
380     * 解析提交过来的表单元素对应的名称以及值<br>
381     * 实现说明:如果表单元素的是对应格式为:<br>name="表单元素名"</br> 格式
382     * 表单元素名和具体的输入值中间是以两个重复的两个字符隔开,
383     * 对应char值为13,10,转换后的字符对应的最后四个字符也是格式字符,获取对应中间的字符即为
384     * 表单元素的输入值
385     * 通过处理可以拆分出对应的表单元素名以及输入值 
386     * @param s   以GBK编码方式组成的包含表单元素名和值的字符串   
387     */   
388    private void readParameter(String s) {
389        String paraName = "";
390        String paraValue = "";
391        int istartlen = -1;
392        int iendlen = -1;
393
394        if ((istartlen = s.indexOf(SIGN_FORMELEMENT)) >= 0) {
395            String tempstr = s.substring(istartlen + SIGN_FORMELEMENT.length()
396                    + 1);
397            int nameindex = tempstr.indexOf("\"");
398            paraName = tempstr.substring(0, nameindex);
399            paraValue = tempstr.substring(nameindex + 5, tempstr.length() - 4);
400            addElement(paratable, paraName, paraValue);
401        }
402    }
403
404}
组件简单说明:
       上传路径在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>


测试运行基本正常,总算解决了心中的一个很长时间的困惑。

你可能感兴趣的:(java,算法,jsp,servlet,360)