beauty_beast

          上善若水 厚德載物

          采用HTTP協議上傳文件實現(java)

          Posted on 2005-10-13 21:41 柳隨風 閱讀(7126) 評論(1)  編輯  收藏
              j2ee開發也好幾年,文件上傳功能基本都是用的第三方的組件,雖然知道其原理,但一直不知道具體是如何實現的,最近有時間,正好同事開發遇到這方面的問題,查了點資料,基本明白了具體實現,為了備忘,就寫下這篇隨筆。
              首先說說同事遇到的問題,最近的項目是使用webwork開發的,同事需要實現多文件上傳的功能,但是
          webwork原則上支持三種上傳解析 pell,cos,jakarta,三種都有自身的優勢和不足,使用發現:
          在webwork中只有pell支持中文文件路徑,但是使用該方式只能處理一個上傳文件,
          而其他兩種雖然支持多文件上傳,但中文支持不好。

          上述的見解都是本人的個人認識,可能支持只是我不知道,如果有誰知道不妨指導一下,不勝感激。
          上傳解析的實現簡單說一下:
                 通過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>



          測試運行基本正常,總算解決了心中的一個很長時間的困惑。

          注:以上的程序只是本人學習的代碼,有很多欠缺的地方,歡迎大家指正,不甚感激。
                   上面代碼只供參考,嘿嘿出了問題我就不負責了。

          最近沒啥事,又出差在外,唉日子過得好慢!!!!!

















            




















                  

          Feedback

          # re: 采用HTTP協議上傳文件實現(java)  回復  更多評論   

          2006-09-27 14:28 by testdfsdgf
          good

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 克什克腾旗| 化隆| 浪卡子县| 阿克陶县| 北海市| 石河子市| 昌乐县| 建阳市| 韶关市| 沙河市| 北辰区| 隆子县| 洪洞县| 凉城县| 尤溪县| 津市市| 瑞金市| 穆棱市| 三明市| 杭锦后旗| 三江| 四平市| 海兴县| 宁夏| 庐江县| 汤原县| 洱源县| 柞水县| 梁平县| 太仆寺旗| 巫山县| 菏泽市| 山西省| 新龙县| 稷山县| 会同县| 苗栗市| 登封市| 南漳县| 陆河县| 青海省|