<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    javaGrowing

      BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
      92 隨筆 :: 33 文章 :: 49 評論 :: 0 Trackbacks
    請求分析
      要實現(xiàn)文件上載,我們必須先了解上載文件的HTTP請求。下面這個簡單的應(yīng)用示范了如何上載文件以及把HTTP請求的原始數(shù)據(jù)寫入文件。
    用文本編輯器查看該文件即可了解請求的格式,在此基礎(chǔ)上我們就可以提取出上載文件的名字、文件內(nèi)容以及原本混合在一起的其他信息。

      這個簡單的應(yīng)用是開發(fā)真正文件上載JavaBean的準(zhǔn)備工作。它由三個文件構(gòu)成:HTML文件main.html,JSP頁面Jsp1.jsp,JavaBean文件SimpleBean.java。

      main.html提供一個表單,用戶從這里選擇文件并把文件上載到服務(wù)器。main.html的代碼如下:
    <html>
    <head>
    <title>文件上載</title>
    </head>
    <body>
    <form action="jsp1.jsp" enctype="MULTIPART/FORM-DATA" method=post>
    作者: <input type="text" name="author" />
    <br />
    公司: <input type="text" name="company" />
    <br />
    選擇要上載的文件 <input type="file" name="filename" />
    <br />
    <input type="submit" value="上載" />
    </form>
    </body>
    </html>

      可以看到,<form>標(biāo)記有一個enctype屬性,屬性值是"MULTIPART/FORM-DATA"。包括提交按鈕在內(nèi),表單里面共有4個輸入元素。前面兩個輸入元素是普通的text元素,即author和company。第三個輸入元素的type屬性是file,這個輸入元素就是用來選擇文件的元素。

      表單的action屬性值是Jsp1.jsp,這意味著請求(包括上載的文件)將發(fā)送給Jsp1.jsp文件。Jsp1.jsp簡單地調(diào)用名為SimpleBean的JavaBean。
    <jsp:useBean id="TheBean" scope="page" class="SimpleBean " />
    <%
    TheBean.doUpload(request);
    %>

    下面是SimpleBean的實現(xiàn)代碼:
    import java.io.*;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.ServletInputStream;

    public class FileUploadBean {
    public void doUpload(HttpServletRequest request) throws
    IOException {
    PrintWriter pw = new PrintWriter(
    new BufferedWriter(new FileWriter("Demo.out")));
    ServletInputStream in = request.getInputStream();
    int i = in.read();
    while (i != -1) {
    pw.print((char) i);
    i = in.read();
    }
    pw.close();
    }
    }

    這個JavaBean把HttpServletRequest對象的表單原始數(shù)據(jù)寫入Demo.out文件。應(yīng)用的用戶界面由main.html文件提供,如下圖所示。

      我們選擇上載的文件是abisco.html。選擇上載HTML文件是為了便于觀察上載后的格式,因為HTML文件本質(zhì)上是文本文件,我們可以方便地瀏覽其內(nèi)容。abisco.html文件的內(nèi)容如下:
    <html>
    <head>
    <title>Abisco</title>
    </head>
    </html>
      點擊“上載”按鈕之后,表單就發(fā)送給了Jsp1.jsp文件,一起發(fā)送的還有abisco.html文件。Jsp1.jsp文件不會向瀏覽器發(fā)送任何應(yīng)答內(nèi)容,但它會生成一個Demo.out文件。
      打開Demo.out文件,我們可以看到如下內(nèi)容:
    -----------------------------7d15340138
    Content-Disposition: form-data; name="Author"
    A. Christie
    -----------------------------7d15340138
    Content-Disposition: form-data; name="Company"
    Abisco
    -----------------------------7d15340138
    Content-Disposition: form-data; name="Filename"; filename="C:123dataabisco.html"
    Content-Type: text/html
    <html>
    <head>
    <title>Abisco</title>
    </head>
    </html>
    -----------------------------7d15340138--
      可以看到,HTTP請求體內(nèi)包含了所有的表單輸入,包括上載的文件。這些輸入數(shù)據(jù)的分隔由一個分隔符實現(xiàn)。分隔符由一系列的“-”字符和一個隨機數(shù)字構(gòu)成。在上面的例子中,分隔符為“-----------------------------7d15340138”。最后一個分隔符結(jié)束請求體,這個分隔符的后面多出兩個“-”符號。

      對于非文件類型的輸入數(shù)據(jù),分隔符后面跟著下面這行內(nèi)容:Content-Disposition: form-data; name=inputName。其中inputName是表單元素的名字。例如:Content-Disposition: form-data; name="Author"。在這行內(nèi)容的后面,緊跟著兩個連續(xù)的回車換行符和表單元素值。

      而對于文件型輸入域,分隔符的后面有兩行內(nèi)容。第一行內(nèi)容包含輸入元素的名字以及上載文件在客戶端的完整路徑,如上例中這行內(nèi)容是“Content-Disposition: form-data; name="Filename"; filename="C:123dataabisco.html"”。這行內(nèi)容指出文件輸入元素的名字是filename,文件的路徑是“C:123dataabisco.html”。注意Windows瀏覽器會設(shè)置文件路徑,而Unix/Linux以及Mac瀏覽器只發(fā)送文件名字。

      第二行包含了文件的內(nèi)容類型,因此它的具體內(nèi)容和上載的文件有關(guān)。本例中第二行的內(nèi)容是“Content-Type: text/html”。

      和非文件輸入元素一樣,文件內(nèi)容在兩個連續(xù)的回車換行符之后正式開始。

    上載文件
      眾所周知,JavaBean是Java平臺的軟件組件,下面要實現(xiàn)的上載功能就是用JavaBean實現(xiàn),所以它可以方便地應(yīng)用到任何需要文件上載功能的應(yīng)用之中。

    代碼清單如下:
    package com.brainysoftware.web;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.ServletInputStream;
    import java.util.Dictionary;
    import java.util.Hashtable;
    import java.io.PrintWriter;
    import java.io.BufferedWriter;
    import java.io.FileWriter;
    import java.io.IOException;

    public class FileUploadBean {
    private String savePath, filepath, filename, contentType;
    private Dictionary fields;

    public String getFilename() {
    return filename;
    }

    public String getFilepath() {
    return filepath;
    }

    public void setSavePath(String savePath) {
    this.savePath = savePath;
    }

    public String getContentType() {
    return contentType;
    }

    public String getFieldValue(String fieldName) {
    if (fields == null || fieldName == null)
    return null;
    return (String) fields.get(fieldName);
    }

    private void setFilename(String s) {
    if (s==null)
    return;
    int pos = s.indexOf("filename="");
    if (pos != -1) {
    filepath = s.substring(pos+10, s.length()-1);
    // Windows瀏覽器發(fā)送完整的文件路徑和名字
    // 但Linux/Unix和Mac瀏覽器只發(fā)送文件名字
    pos = filepath.lastIndexOf("\");
    if (pos != -1)
    filename = filepath.substring(pos + 1);
    else
    filename = filepath;
    }
    }

    private void setContentType(String s) {
    if (s==null)
    return;
    int pos = s.indexOf(": ");
    if (pos != -1)
    contentType = s.substring(pos+2, s.length());
    }

    public void doUpload(HttpServletRequest request) throws IOException {
    ServletInputStream in = request.getInputStream();
    byte[] line = new byte[128];
    int i = in.readLine(line, 0, 128);
    if (i < 3)
    return;
    int boundaryLength = i - 2;
    String boundary = new String(line, 0, boundaryLength); //-2丟棄換行字符
    fields = new Hashtable();

    while (i != -1) {
    String newLine = new String(line, 0, i);
    if (newLine.startsWith("Content-Disposition: form-data; name="")) {
    if (newLine.indexOf("filename="") != -1) {
    setFilename(new String(line, 0, i-2));
    if (filename==null)
    return;

    //文件內(nèi)容
    i = in.readLine(line, 0, 128);
    setContentType(new String(line, 0, i-2));
    i = in.readLine(line, 0, 128);

    //空行
    i = in.readLine(line, 0, 128);
    newLine = new String(line, 0, i);
    PrintWriter pw = new PrintWriter(new BufferedWriter(new
    FileWriter((savePath==null? "" : savePath) + filename)));
    while (i != -1 && !newLine.startsWith(boundary)) {
    // 文件內(nèi)容的最后一行包含換行字符

    // 因此我們必須檢查當(dāng)前行是否是最

    // 后一行
    i = in.readLine(line, 0, 128);
    if ((i==boundaryLength+2 || i==boundaryLength+4)
    && (new String(line, 0, i).startsWith(boundary)))
    pw.print(newLine.substring(0, newLine.length()-2));
    else
    pw.print(newLine);
    newLine = new String(line, 0, i);
    }
    pw.close();
    }
    else {
    // 普通表單輸入元素
    // 獲取輸入元素名字
    int pos = newLine.indexOf("name="");
    String fieldName = newLine.substring(pos+6, newLine.length()-3);
    i = in.readLine(line, 0, 128);
    i = in.readLine(line, 0, 128);
    newLine = new String(line, 0, i);
    StringBuffer fieldValue = new StringBuffer(128);
    while (i != -1 && !newLine.startsWith(boundary)) {
    // 最后一行包含換行字符
    // 因此我們必須檢查當(dāng)前行是否是最后一行
    i = in.readLine(line, 0, 128);
    if ((i==boundaryLength+2 || i==boundaryLength+4)
    && (new String(line, 0, i).startsWith(boundary)))
    fieldValue.append(newLine.substring(0, newLine.length()-2));
    else
    fieldValue.append(newLine);
    newLine = new String(line, 0, i);
    }
    fields.put(fieldName, fieldValue.toString());
    }
    }
    i = in.readLine(line, 0, 128);
    }
    }
    }
      代碼的第一行是包聲明,如果你不想讓該類從屬于任何包,可以刪除這行代碼。接下來的幾行代碼聲明了該JavaBean所要引用的各個類和接口。
      FileUploadBean類有5個私有的屬性(域),6個公用的方法,2個私有的方法。

    屬性
    FileUploadBean類的5個域都是私有的,它們是:
    private String savePath

    該域指定了文件上載后保存到服務(wù)器的哪一個路徑。savePath的值用setSavePath方法設(shè)置。這個值應(yīng)該在調(diào)用doUpload方法之前設(shè)置;如沒有設(shè)置,上載后的文件將保存到服務(wù)器的默認(rèn)目錄。

    private String filepath
    該域指定了上載文件在客戶端的完整路徑。filepath的值由doUpload方法設(shè)置,在JSP頁面或者Servlet中調(diào)用getFilepath方法可以獲取filepath域的值。對于非Windows下的瀏覽器,該值等于filename。

    private String filename
    該域是上載文件的名字。filename的值由setFilename方法設(shè)置。在JSP或者Servlet中調(diào)用getFilename方法可以獲取filename域的值。

    private String contentType
    該域是上載文件的內(nèi)容類型。contentType的值由doUpload方法設(shè)置,你可以用getContentType方法獲得contentType域的值。

    private Dictionary fields
    fields域保存了用戶在表單中輸入數(shù)據(jù)的名字/值對。調(diào)用getFieldValue方法可以獲取表單輸入元素的值。

    方法
      前面四個public類型的方法用于返回FileUploadBean對象的私有域,它們是:getFilepath,getFilename,getContentType以及getFieldValue。

    public String getFilepath()
    返回filepath私有域的值。

    public String getFilename()
    返回filename私有域的值。

    public String getContentType()
    返回contentType私有域的值。

    public String getFieldValue(String fieldName)
    返回HTML表單中指定輸入元素的值,元素的名字通過fieldName參數(shù)指定。

    public void setSavePath(String savePath)
    用該方法指定服務(wù)器上保存上載文件的目錄的名字。

    public void doUpload(HttpServletRequest request) throws IOException
    doUpload是FileUploadBean類中最重要的一個方法。它的任務(wù)有二個:第一,它從HTML表單提取出輸入域的名字和值并保存到Dictionary對象;第二,doUpload方法提取出上載的文件,把這個文件保存到savePath指定的路徑,并分別把文件的名字、路徑、內(nèi)容類型賦給filename、filepath和contentType域。

    private void setContentType(String s)
    由doUpload方法調(diào)用。setContentType方法從原始字節(jié)數(shù)據(jù)提取出上載文件的內(nèi)容類型。

    private void setFilename(String s)
    由doUpload方法調(diào)用。setFilename方法從原始字節(jié)數(shù)據(jù)提取出文件路徑和名字。
      doUpload方法的參數(shù)是Servlet/JSP容器創(chuàng)建的HttpServletRequest對象。HttpServletRequest對象描述了程序為了提取出HTML表單元素名字-值對以及上載文件必須處理的HTTP請求。doUpload方法首先通過HttpServletRequest對象的getInputStream方法獲得ServletInputStream對象。

      如前所述,每一個表單元素由分界符和一組回車換行符分隔。因此,我們可以一行一行地讀入HttpServletRequest對象的內(nèi)容。下面這行代碼定義了一個名為line的byte數(shù)組:
    byte[] line = new byte[128];

      然后,我們用ServletInputStream對象的readLine方法讀入HttpServletRequest對象內(nèi)容的第一行:
    int i = in.readLine(line, 0, 128);

      第一行應(yīng)該是分界符,而且如果沒有錯誤的話,它的長度應(yīng)該大于3。如果它的長度小于3,我們可以認(rèn)為出現(xiàn)了錯誤,doUpload方法應(yīng)該立即返回:
    if (i < 3)
    return;

      分界符和分界符的長度都非常重要,從本文后面你可以看到這一點。分界符由一組回車換行符結(jié)束,因此它的實際長度要比readLine方法返回的字節(jié)數(shù)少2。

    int boundaryLength = i - 2;
      丟棄byte數(shù)組line的最后2個回車換行符即可獲得分界符:

    String boundary = new String(line, 0, boundaryLength);
      接下來,fields域被實例化成Hashtable對象。這個Hashtable對象將用來保存HTML表單元素的名字/值對。

    fields = new Hashtable();
      由于已經(jīng)有了分界符,接下來我們就可以開始提取出表單元素的值。具體方法是用一個while循環(huán)按行讀入HttpServletRequest對象的內(nèi)容,直至遇到內(nèi)容結(jié)束readLine方法返回-1為止。所有的表單元素都以分界符開始,后面跟上“Content-Disposition”行,這一行由下面這些字符開始:

    Content-Disposition: form-data; name=
      表單元素有兩種類型:文件,非文件(普通的表單元素,如TEXT或者HIDDEN元素)。這兩種表單元素的區(qū)別在于文件元素包含字符串“filename="filename"”。由此,我們可以利用該信息把文件和非文件的表單輸入元素區(qū)別開來,代碼如下:
    if (newLine.startsWith("Content-Disposition: form-data; name="")) {
    if (newLine.indexOf("filename="") != -1) {

    // 文件型表單輸入元素
    // 這里加上提取文件的代碼
    . . .
    }
    else {
    // 普通表單輸入元素
    // 這里加上提取表單元素的代碼
    . . .
    }
    }

      現(xiàn)在,我們首先來看看提取文件內(nèi)容的代碼。
      文件路徑包含在“Content-Disposition”的后面。為提取文件路徑和文件名字,doUpload方法調(diào)用了setFilename私有方法。setFilename方法提取出文件路徑和文件名字信息,然后把它們賦值給filepath和filename域。調(diào)用setFilename方法之后,filename域應(yīng)該不再是null。如果此時filename域仍舊是null,則說明遇到了問題,doUpload方法直接返回。

    if (filename==null)
    return;

      “Content-Disposition”行之后的下一行是內(nèi)容類型行。因此,doUpload方法接著調(diào)用readLine方法,然后調(diào)用setContentType私有方法。setContentType方法和setFilename方法相似,它從原始字節(jié)數(shù)據(jù)中提取出上載文件的內(nèi)容類型并保存到contentType域。

      緊接內(nèi)容類型行的下一行是空行,因此程序再調(diào)用了一次readLine方法。

    i = in.readLine(line, 0, 128);
      接下來開始了真正的文件內(nèi)容。我們先應(yīng)該做好通過PrintWriter對象把文件寫入磁盤的準(zhǔn)備。

    PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(( savePath==null? "" : savePath ) + filename)));

      上載文件保存到哪個位置取決于savePath域是否已經(jīng)設(shè)置。如果savePath域沒有設(shè)置,它的值是null,則文件將被保存到默認(rèn)目錄;如果savePath域已經(jīng)設(shè)置,它的值不是null,則上載的文件被保存到它所指定的目錄。

      然后我們就可以提取文件的內(nèi)容。具體方法是使用while循環(huán),每次循環(huán)讀入一行內(nèi)容并通過PrintWriter的輸出方法把它寫入磁盤。但我們知道,文件的最后一行包含兩個回車換行符號,所以保存到磁盤的字節(jié)數(shù)據(jù)不應(yīng)該包含這兩個字符。因此,如果讀入的行不是文件的最后一行,我們把所有讀到的字節(jié)數(shù)據(jù)寫入磁盤;如果讀入的行已經(jīng)是文件的最后一行,寫入磁盤的字節(jié)數(shù)據(jù)要減去最后兩個字符。

      然而,我們并不知道文件的大小,我們只知道緊接文件內(nèi)容的下一行又是一個分界符;或者,如果文件是最后一個HTML表單元素,接下來的一行是分界符加上兩個短劃線字符。因此,只要檢查下一行內(nèi)容是否是分界符,我們就知道了何時應(yīng)該結(jié)束while循環(huán)。這就是前面說分界符很重要的原因,在這里我們必須用到分界符。

      雖然我們可以讀取下一行內(nèi)容然后用startsWith方法檢查它是否是一個分界符,然而,由于字符串操作的開銷非常大,為了減少字符串操作,我們比較readLine讀入的字節(jié)數(shù)組的長度。后者應(yīng)該等于boundaryLength + 2;或者,如果它是HttpServletRequest對象中的最后一行,由于多出了最后兩個短劃線字符,它應(yīng)該等于boundaryLength + 4。由于一行內(nèi)容即使不是分界符也可以和分界符一樣長,當(dāng)長度匹配之后我們又將它與分界符比較。這就是前面提到boundaryLength很重要的原因了。

      整個處理過程的實現(xiàn)代碼如下:
    while (i != -1 && !newLine.startsWith(boundary)) {
    i = in.readLine(line, 0, 128);
    if ((i==boundaryLength+2 || i==boundaryLength+4) && (new String(line, 0, i).startsWith(boundary)))
    pw.print(newLine.substring(0, newLine.length()-2));
    else pw.print(newLine);

    newLine = new String(line, 0, i);
    }

      把文件內(nèi)容保存到磁盤之后,我們關(guān)閉了PrintWriter。
    pw.close();
    非文件的表單元素也可以用類似的方法提取。不同之處在于,此時我們不再把數(shù)據(jù)寫入磁盤,而是把名字-值對保存到Dictionary對象。

    fields.put(fieldName, fieldValue.toString());

    應(yīng)用實例
      編譯好Bean之后,我們就可以從Servlet或者JSP頁面中使用它了。可能你在Tomcat之類的Servlet/JSP環(huán)境下使用Bean,部署B(yǎng)ean最簡單的方法是把class文件壓縮成jar文件,然而把jar文件放到Tomcat的lib目錄下。要讓Tomcat裝入jar文件,你必須重新啟動Tomcat。

      下面是一個HTML文件和一個JSP文件,它們示范了這個Bean的應(yīng)用。HTML文件包含一個表單以及幾個輸入元素:
    <html>
    <head>
    <title>文件上載</title>
    </head>
    <body>
    <form action=jsp1.jsp enctype="MULTIPART/FORM-DATA" method=post>
    作者: <input type=text name=author>
    <br>
    公司: <input type=text name=company>
    <br>
    說明: <textarea name=comment></textarea>
    <br>
    選擇要上載的文件<input type=file name=filename>
    <br>
    文件描述: <input type=text name=description>
    <br>
    <input type=submit value="Upload">
    </form>
    </body>
    </html>

      用戶提交上述表單之后,該HTTP請求將由Jsp1.jsp處理。Jsp1.jsp運用FileUpload這個Bean來處理請求。Jsp1.jsp的代碼如下:
    <%@ page contentType="text/html;charset=gb2312"%>
    <jsp:useBean id="TheBean" scope="page"
    class="com.brainysoftware.web.FileUploadBean" />
    <%
    TheBean.doUpload(request);
    out.println("Filename:" + TheBean.getFilename());
    out.println("<BR>內(nèi)容類型:" + TheBean.getContentType());
    out.println("<BR>作者:" + TheBean.getFieldValue("Author"));
    out.println("<BR>公司:" + TheBean.getFieldValue("Company"));
    out.println("<BR>說明:" + TheBean.getFieldValue("Comment"));
    %>
    posted on 2006-11-03 13:12 javaGrowing 閱讀(1257) 評論(0)  編輯  收藏 所屬分類: jsp學(xué)習(xí)
    主站蜘蛛池模板: 亚洲一卡二卡三卡| 色屁屁在线观看视频免费| 免费a级毛片高清视频不卡 | 亚洲最大天堂无码精品区| 国产美女精品视频免费观看 | 亚洲av无码专区在线观看下载| 又大又粗又爽a级毛片免费看| 免费在线中文日本| 亚洲欧美国产欧美色欲| 国产成人综合亚洲亚洲国产第一页| 在免费jizzjizz在线播| 国产亚洲精品美女久久久久久下载| 亚洲成色www久久网站夜月| 午夜视频免费成人| 久久精品电影免费动漫| 亚洲av乱码一区二区三区按摩| 久久精品国产96精品亚洲 | 亚洲精品国产免费| 亚州**色毛片免费观看| 亚洲制服丝袜一区二区三区| 亚洲国产精品久久人人爱| 日本特黄特色免费大片| 久久免费观看国产精品| 香港特级三A毛片免费观看| 中文字幕亚洲免费无线观看日本 | 亚洲欧洲一区二区三区| 免费精品国产日韩热久久| 国产在线播放线91免费| 久久亚洲中文字幕无码| 亚洲人成高清在线播放| 亚洲乱亚洲乱妇无码麻豆| 国产网站在线免费观看| 成年人网站免费视频| 亚洲一区在线视频观看| 亚洲国产另类久久久精品黑人| 人妻无码一区二区三区免费 | 亚洲精品国产综合久久久久紧| 亚洲欧洲日产国码久在线观看| 亚洲人AV永久一区二区三区久久| 无码专区永久免费AV网站| 亚洲AV无码一区二区乱子仑|