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以及各種瀏覽器的行為,總是會出現這樣或者那樣的問題。

-----------------------------7dc1e411c05fe
Content-Disposition: form-data; name= " upfile "; filename= " C:\Documents and Settings\Administrator\桌面\小舅好啊.doc "
Content-Type: application/msword
上面這個是文件流開始的元數據信息
 
multipart/form-data; boundary=---------------------------7dc1e411c05fe
這個是通過Request獲取到的Content-Type,根據這個判斷是否MultiPart,注意不僅僅可以傳doc,應該MIME的都行,boundary很重要,這個就是處理文件流開始和結尾的。

通過分析流,發現其實郵件傳輸和這個大同小異,除了郵件使用了Base64編碼吧?印象中好象是。

在文件流結尾會有這個:

-------------------------------7dc1e411c05fe
因此,通過分析boundary確定邊界、通過分析Content-Disposition獲取文件元信息,當然了,也可以不傳文件,這樣僅僅什么也不做而已。

 

分析完了,奉上我第一版本的粗糙代碼,說實話,我這個就是為了研究原理,里面代碼存在很大問題,先說出來,別讓大家噴我:

 

1、編碼格式,編碼格式很重要,文件名可能是中文,因此需要設置UTF-8或者GBK,但是在解析流的時候,Encoding只能在解析元數據信息的時候的使用,其余時候

各位還是安心使用ASCII就是字節流吧,這樣原來文件是什么,存儲的就是什么,肯定不會出現問題,之前我用GBK了,就帶來Excel上傳每次都有格式問題。

2、流處理問題,緩沖流的操作,並且讀取流需要分階段來處理,先處理元數據信息,獲取完后在處理文件真實信息,避免過多用戶上傳導致程序繁忙。

 

JSP如下,一個簡單的JSP

 

<%@ page language="java" contentType="text/html; charset=gb2312"
    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:

public  class UploadFile  extends HttpServlet {
     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的例子,看看這個多簡潔:

public  class UploadFileEx  extends HttpServlet {
     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://www.servlets.com/cos/

http://commons.apache.org/fileupload/

希望對大家有所幫助。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM