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

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

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

    瘋狂

    STANDING ON THE SHOULDERS OF GIANTS
    posts - 481, comments - 486, trackbacks - 0, articles - 1
      BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

    struts2 文件上傳和下載,以及部分源碼解析

    Posted on 2010-06-02 11:37 瘋狂 閱讀(8468) 評論(5)  編輯  收藏 所屬分類: java strutsweb

    struts2 文件上傳 和部分源碼解析,以及一般上傳原理

    (1)  單文件上傳 

    一.簡介

    Struts2并未提供自己的請求解析器,也就是就Struts2不會自己去處理multipart/form-data的請求,它需要調(diào)用其他請求解析器,將HTTP請求中的表單域解析出來。但Struts2在原有的上傳解析器基礎(chǔ)

    上做了進(jìn)一步封裝,更進(jìn)一步簡化了文件上傳。
    Struts2默認(rèn)使用的是Jakarta的Common-FileUpload框架來上傳文件,因此,要在web應(yīng)用中增加兩個(gè)Jar文件:commons-fileupload-1.2.jar和commons-io-1.3.1.jar。它在原上傳框架上做了進(jìn)一步封裝

    ,簡化了文件上傳的代碼實(shí)現(xiàn),取消了不同上傳框架上的編程差異。
    如果要改成其它的文件上傳框架,可以修改struts.multipart.parser常量的值為cos/pell,默認(rèn)值是jakata。并在classpath中增加相應(yīng)上傳組件的類庫

    例如配置成cos上傳

    struts.multipart.parser=cos

    struts.multipart.maxSize=1024  指定文件的最大字結(jié)數(shù)

    二.原理

    不管用common-fileUPload框架,還是用cos,都是通過將HTTP的數(shù)據(jù)保存到臨時(shí)文件夾,然后Struts使用fileUpload攔截器將文件綁定到Action的實(shí)例中。
    也就是配置文件的 <interceptor-ref name="fileUpload"/>
    我們可以通過源代碼struts2-code-XX.jar的struts-default.xml文件找到
    <interceptor name="fileUpload" class="org.apache.struts2.interceptor.FileUploadInterceptor"/>
    打開這個(gè)類的源代碼可以看見相關(guān)如下:
    /**
     * <!-- START SNIPPET: description -->
     * <p/>
     * Interceptor that is based off of {@link MultiPartRequestWrapper}, which is automatically applied for any request that
     * includes a file. It adds the following parameters, where [File Name] is the name given to the file uploaded by the
     * HTML form:
     * <p/>
     * <ul>
     * <p/>
     * <li>[File Name] : File - the actual File</li>
     * <p/>
     * <li>[File Name]ContentType : String - the content type of the file</li>
     * <p/>
     * <li>[File Name]FileName : String - the actual name of the file uploaded (not the HTML name)</li>
     * <p/>
     * </ul>
     * <p/>
    也就是說我們需要三個(gè)變量File(表單的name),其他兩個(gè)參數(shù)通過set個(gè)體方法有strtus調(diào)用

    接著下面是一些國際化提示的東西:
    * processed for all i18n requests. You can override the text of these messages by providing text for the following
     * keys:
     * <li>struts.messages.error.uploading - a general error that occurs when the file could not be uploaded</li>
     * <p/>
     * <li>struts.messages.error.file.too.large - occurs when the uploaded file is too large</li>
     * <p/>
     * <li>struts.messages.error.content.type.not.allowed - occurs when the uploaded file does not match the expected
     * content types specified</li>
     * <p/>
     * <li>struts.messages.error.file.extension.not.allowed - occurs when the uploaded file does not match the expected
     * file extensions specified</li>
     * <p/>
    例如struts.messages.error.content.type.not.allowed 表示文件類型錯(cuò)誤:也就是說如果我們給攔截器配置了屬性allowedTypes 例如:
    <param name ="allowedTypes">image/bmp,image/png,image/gif,image/jpeg,image/jpg</param>    但是上傳的時(shí)候沒有上傳規(guī)定的類型
    struts2就會去我們的資源文件去找key為struts.messages.error.content.type.not.allowed的國際化資源給與提示這時(shí)候我們可以在我們的資源中配置這個(gè)key:
    例如:struts.messages.error.content.type.not.allowed=您上傳的文件類型只能為...!請重新選擇!
    (當(dāng)然需要<constant name="struts.custom.i18n.resources" value="globalMessages"/>)globalMessages為資源前綴,然后通過:<s:fielderror/>來顯示提示
    <!-- START SNIPPET: parameters -->
     * <p/>
     * <ul>
     * <p/>
     * <li>maximumSize (optional) - the maximum size (in bytes) that the interceptor will allow a file reference to be set
     * on the action. Note, this is <b>not</b> related to the various properties found in struts.properties.
     * Default to approximately 2MB.</li>
     * <p/>
     * <li>allowedTypes (optional) - a comma separated list of content types (ie: text/html) that the interceptor will allow
     * a file reference to be set on the action. If none is specified allow all types to be uploaded.</li>
     * <p/>
     * <li>allowedExtensions (optional) - a comma separated list of file extensions (ie: .html) that the interceptor will allow
     * a file reference to be set on the action. If none is specified allow all extensions to be uploaded.</li>
     * </ul>
     * <p/>
    上面則是攔截器的相關(guān)參數(shù),一目了然:maximumSize 上傳文件最大多少 默認(rèn):2MB。allowedTypes容許的上傳類型。allowedExtensions容許的擴(kuò)展名
    接著是相關(guān)action的代碼說明:
    <!-- START SNIPPET: example-action -->
     *    package com.example;
     * <p/>
     *    import java.io.File;
     *    import com.opensymphony.xwork2.ActionSupport;
     * <p/>
     *    public UploadAction extends ActionSupport {
     *       private File file;
     *       private String contentType;
     *       private String filename;
     * <p/>
     *       public void setUpload(File file) {
     *          this.file = file;
     *       }
     * <p/>
     *       public void setUploadContentType(String contentType) {
     *          this.contentType = contentType;
     *       }
     * <p/>
     *       public void setUploadFileName(String filename) {
     *          this.filename = filename;
     *       }
     * <p/>
     *       public String execute() {
     *          //...
     *          return SUCCESS;
     *       }
     *  }
    其實(shí)最主要的是set方法的確定:我們跟蹤到大約238行:
     String contentTypeName = inputName + "ContentType";
     String fileNameName = inputName + "FileName";
    最終確定我們的private File file;屬性名稱可以隨便,
    但是filenam和contenttype的set方法要有規(guī)定 例如:
    如果private File myFile;

    則對應(yīng)的其他的兩個(gè)屬性set方法如下:

    public void setMyFileContentType(String contentType) {
             this.contentType = contentType;//當(dāng)然contentType可以隨便起名 最終要的是set+MyFile+ContentType方法
        }

    public void setMyFileFileName(String filename) {
               this.filename = filename;/當(dāng)然filename可以隨便起名 最終要的是set+MyFile+FileName方法
           }
    以下是實(shí)例:
    三.需要的jar包(默認(rèn)使用commons-fileupload,如果使用cos,要將jar引進(jìn)來)

    commons-logging-1.1.jar
    freemarker-2.3.8.jar
    ognl-2.6.11.jar
    struts2-core-2.0.6.jar
    xwork-2.0.1.jar
    commons-io-1.3.1.jar
    commons-fileupload-1.2.jar

    四.實(shí)例

    1.首先,創(chuàng)建上傳頁面

    Html代碼
    1.<%@page language="java" contentType = "text/html; charset=utf-8" pageEncoding = "utf-8"%>   
    2.<%@taglib prefix="s" uri ="/struts-tags"%>   
    3. 
    4.<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 
    5.<html> 
    6.  <head> 
    19.  </head> 
    20.    
    21.  <body> 
    22.   <s:form action ="fileUpload" method ="POST" enctype ="multipart/form-data">   
    23.                <s:fielderror /> 
    24.        <s:file name ="upload"/>     
    25.        <s:submit />   
    26.    </s:form >   
    27. 
    28.  </body> 
    29.</html> 

    2.action


    1.package com;     
    2.    
    3.import java.io.BufferedInputStream;     
    4.import java.io.BufferedOutputStream;     
    5.import java.io.File;     
    6.import java.io.FileInputStream;     
    7.import java.io.FileOutputStream;     
    8.import java.io.InputStream;     
    9.import java.io.OutputStream;     
    10.import java.util.Date;     
    11.    
    12.import org.apache.struts2.ServletActionContext;     
    13.    
    14.import com.opensymphony.xwork2.ActionSupport;     
    15.    
    16.public class FileUploadAction extends ActionSupport {     
    17.         
    18.    private static final long serialVersionUID = 6452146812454l;     
    19.    
    20.    private File upload;     
    21.    
    22.    private String uploadContentType;     
    23.    
    24.    private String uploadFileName;     
    25.    
    26.    private String imageFileName;     
    27.    
    28.    public String getUploadContentType() {     
    29.        return uploadContentType;     
    30.    }     
    31.    
    32.    public void setUploadContentType(String uploadContentType) {     
    33.        this.uploadContentType = uploadContentType;     
    34.    }     
    35.    
    36.    public File getUpload() {     
    37.        return upload;     
    38.    }     
    39.    
    40.    public void setUpload(File upload) {     
    41.        this.upload = upload;     
    42.    }     
    43.    
    44.    public String getUploadFileName() {     
    45.        return uploadFileName;     
    46.    }     
    47.    
    48.    public void setUploadFileName(String uploadFileName) {     
    49.        this.uploadFileName = uploadFileName;     
    50.    }     
    51.    
    52.    public void setImageFileName(String imageFileName) {     
    53.        this.imageFileName = imageFileName;     
    54.    }     
    55.    
    56.    public String getImageFileName() {     
    57.        return imageFileName;     
    58.    }     
    59.    
    60.    private static void copy(File src, File dst) {     
    61.        try {     
    62.            InputStream in = null;     
    63.            OutputStream out = null;     
    64.            try {     
    65.                in = new BufferedInputStream(new FileInputStream(src));     
    66.                out = new BufferedOutputStream(new FileOutputStream(dst));     
    67.                byte[] buffer = new byte[1024*10];     
    68.                while (in.read(buffer) > 0) {     
    69.                    out.write(buffer);     
    70.                }     
    71.            } finally {     
    72.                if (null != in) {     
    73.                    in.close();     
    74.                }     
    75.                if (null != out) {     
    76.                    out.close();     
    77.                }     
    78.            }     
    79.        } catch (Exception e) {     
    80.            e.printStackTrace();     
    81.        }     
    82.    }     
    83.    
    84.    @Override    
    85.    public String execute() {     
    86.        System.out.println(uploadFileName);     
    87.             
    88.        imageFileName = System.currentTimeMillis() + uploadFileName.substring(uploadFileName.lastIndexOf("."));     
    89.        File imageFile = new File(ServletActionContext.getServletContext()     
    90.                .getRealPath("/uploadImages")     
    91.                + "/" + imageFileName);     //我們自己重新定義的文件名,也可以直接用 uploadFileName
    92.        copy(upload, imageFile);     
    93.        return SUCCESS;     
    94.    }     
    95.    
    96.}    
    97
    表單的enctype ="multipart/form-data,與一般的上傳一樣.

    <s:file name="upload">會將upload綁定到action的upload,其次他還會將上傳記文件的MIME類型綁定到uploadContentType,文件名綁定到uploadFileName中,他們是通過

    setUploadContentType和setUploadFileName進(jìn)行綁定的,下面進(jìn)行的多文件上傳也是同個(gè)道理,不過要用數(shù)組或者是list來進(jìn)行綁定,然后多個(gè)文件的MIME類型也會綁定到以數(shù)組

    名字加ContentType和FileName的字符串?dāng)?shù)組中。   比如說上傳的文件的數(shù)組名為:File[] uploads,則它們的MIME類型綁定的對應(yīng)的數(shù)組是uploadsFileName和uploadsContentType.

    3.struts.xml的配置

    Xml代碼

    Xml代碼
    1.<?xml version="1.0" encoding="UTF-8" ?>    
    2.<!DOCTYPE struts PUBLIC     
    3.    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"     
    4.    "http://struts.apache.org/dtds/struts-2.0.dtd">    
    5.    
    6.<struts>    
    7.    <constant name="struts.multipart.saveDir" value="/tmp" />    
    8.    <package name="fileUploadDemo" extends="struts-default">    
    9.        <action name ="fileUpload" class ="com.FileUploadAction">      
    10.              
    11.            <interceptor-ref name ="fileUpload">      
    12.                <param name ="allowedTypes">image/bmp,image/png,image/gif,image/jpeg,image/jpg</param>      
    13.            </interceptor-ref>      
    14.            <interceptor-ref name ="defaultStack"/>      
    15.              
    16.            <result name ="input" >/fileUpload.jsp</result>    
    17.            <result name ="success">/showUpload.jsp </result>      
    18.        </action>      
    19.    </package>    
    20.</struts>    

    4.最后是web.xml的配置

    Xml代碼
    1.<?xml version="1.0" encoding="UTF-8"?>    
    2.<web-app version="2.4"      
    3.    xmlns="http://java.sun.com/xml/ns/j2ee"      
    4.    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"      
    5.    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee      
    6.    http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">    
    7.         
    8.    <filter>      
    9.        <filter-name>struts-cleanup</filter-name>      
    10.        <filter-class>      
    11.            org.apache.struts2.dispatcher.ActionContextCleanUp     
    12.        </filter-class>      
    13.    </filter>      
    14.         
    15.    <filter>      
    16.        <filter-name>struts2</filter-name >      
    17.        <filter-class>      
    18.            org.apache.struts2.dispatcher.FilterDispatcher     
    19.        </filter-class >      
    20.    </filter>      
    21.         
    22.    <filter-mapping>      
    23.        <filter-name>struts-cleanup</filter-name >      
    24.        <url-pattern>/*</url-pattern>      
    25.    </filter-mapping>      
    26.    
    27.    <filter-mapping>      
    28.        <filter-name>struts2</filter-name >      
    29.        <url-pattern>/*</url-pattern >      
    30.    </filter-mapping>      
    31.         
    32.  <welcome-file-list>    
    33.    <welcome-file>index.jsp</welcome-file>    
    34.  </welcome-file-list>    
    35.</web-app>  

    (2) 多文件上傳

    多文件上傳
    與單文件上傳相似,實(shí)現(xiàn)多文件你可以將多個(gè)<s:file />綁定Action的數(shù)組或列表。如下例所示。

    < s:form action ="uploadList" method ="POST" enctype ="multipart/form-data" >
        < s:file label ="File (1)" name ="upload" />
        < s:file label ="File (2)" name ="upload" />
        < s:file label ="FIle (3)" name ="upload" />
        < s:submit />
    </ s:form > 清單14 多文件上傳JSP代碼片段
    如果你希望綁定到數(shù)組,Action的代碼應(yīng)類似:

         private File[] uploads;
         private String[] uploadSFileName;
         private String[] uploadSContentType;

    多文件上傳數(shù)組綁定Action代碼片段
    如果你想綁定到列表,則應(yīng)類似:

         private List < File > uploads ;
         private List < String > uploadSFileName ;
         private List < String > uploadSContentType ;
    多文件上傳列表綁定Action代碼片段

    另外是一般上傳文件的原理:當(dāng)然具體可以看http協(xié)議的rfc文檔:
    關(guān)于multipart/form-data 相關(guān)資料可以看;http://www.ietf.org/rfc/rfc1867.txt 大約在[Page 1]的地方有介紹
     表單配置multipart/form-data 說明以二進(jìn)制流的方式傳輸表單字段的數(shù)據(jù):
    我們通過以下代碼看到request數(shù)據(jù)流中的內(nèi)容:

     PrintWriter out = response.getWriter();
      InputStream is = request.getInputStream();
      
      BufferedReader br = new BufferedReader(
       new InputStreamReader(is));
      String buffer = null;
      while( (buffer = br.readLine()) != null)
      {
       //在頁面中顯示讀取到的請求參數(shù)
       out.println(buffer + "<br />");
      }
    out.flush();
    out.close();
    例如:我上傳一個(gè)文件D:\apache-tomcat-6018\bin\version.sh (tomcat版本文件)
    最終頁面顯示:
    -----------------------------7da1052ec05fe
    Content-Disposition: form-data; name="ff"; filename="D:\apache-tomcat-6018\bin\version.sh"
    Content-Type: text/plain

    #!/bin/sh

    # Licensed to the Apache Software Foundation (ASF) under one or more
    # contributor license agreements. See the NOTICE file distributed with
    # this work for additional information regarding copyright ownership.
    # The ASF licenses this file to You under the Apache License, Version 2.0
    # (the "License"); you may not use this file except in compliance with
    # the License. You may obtain a copy of the License at
    #
    # http://www.apache.org/licenses/LICENSE-2.0
    #
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.


    # resolve links - $0 may be a softlink
    PRG="$0"

    while [ -h "$PRG" ] ; do
    ls=`ls -ld "$PRG"`
    link=`expr "$ls" : '.*-> \(.*\)$'`
    if expr "$link" : '/.*' > /dev/null; then
    PRG="$link"
    else
    PRG=`dirname "$PRG"`/"$link"
    fi
    done

    PRGDIR=`dirname "$PRG"`
    EXECUTABLE=catalina.sh

    # Check that target executable exists
    if [ ! -x "$PRGDIR"/"$EXECUTABLE" ]; then
    echo "Cannot find $PRGDIR/$EXECUTABLE"
    echo "This file is needed to run this program"
    exit 1
    fi

    exec "$PRGDIR"/"$EXECUTABLE" version "$@"

    -----------------------------7da1052ec05fe--
    我們發(fā)現(xiàn)我們上傳的內(nèi)容在
    -----------------------------7da1052ec05fe
    Content-Disposition: form-data; name="ff"; filename="D:\apache-tomcat-6018\bin\version.sh"
    Content-Type: text/plain和-----------------------------7da1052ec05fe--中間
    因此我們可以通過以下代碼來獲取上傳內(nèi)容并保存:

    //取得HttpServletRequest的InputStream輸入流
    InputStream is = request.getInputStream();
    BufferedReader br = new BufferedReader(new InputStreamReader(is));
    String buffer = null;
    //循環(huán)讀取請求內(nèi)容的每一行內(nèi)容
    while( (buffer = br.readLine()) != null)
    {
     //如果讀到的內(nèi)容以-----------------------------開始,
     //且以--結(jié)束,表明已到請求內(nèi)容尾
     if(buffer.endsWith("--") && buffer
      .startsWith("-----------------------------"))//length為29
     {
      //跳出循環(huán)
      break;
     }
     //如果讀到的內(nèi)容以-----------------------------開始,表明開始了一個(gè)表單域
     if(buffer.startsWith("-----------------------------"))
     {
      //如果下一行內(nèi)容中有filename字符串,表明這是一個(gè)文件域
      if (br.readLine().indexOf("filename") > 1)
      {
       //跳過兩行,開始處理上傳的文件內(nèi)容
       br.readLine();
       br.readLine();
       //以系統(tǒng)時(shí)間為文件名,創(chuàng)建一個(gè)新文件
       File file = new File(request.getRealPath("/")
        + System.currentTimeMillis());
                             //當(dāng)然我們可以讀取filenam來保存這里簡化
       //創(chuàng)建一個(gè)文件輸出流
       PrintStream ps = new PrintStream(new FileOutputStream(file));
       String content = null;
       //接著開始讀取文件內(nèi)容
       while( (content = br.readLine()) != null)
       {
        //如果讀取的內(nèi)容以-----------------------------開始,
        //表明開始了下一個(gè)表單域內(nèi)容
        if(content.startsWith("-----------------------------"))length為29
        {
         //跳出處理
         break;
        }
        //將讀到的內(nèi)容輸出到文件中
        ps.println(content);
       }
       //關(guān)閉輸出
       ps.flush();
       ps.close();
      }
     }
    }
    br.close();

    關(guān)于strtus2下載:
    下載最終是通過contentType和數(shù)據(jù)流將數(shù)據(jù)輸出到客戶端來實(shí)現(xiàn),在struts中也是通過InputStream和相關(guān)的配置來實(shí)現(xiàn):
    同樣最終到strtus的下載相關(guān)的源代碼:org.apache.struts2.dispatcher.StreamResult我們看到
     
         public static final String DEFAULT_PARAM = "inputName";

        protected String contentType = "text/plain";
        protected String contentLength;
        protected String contentDisposition = "inline";//在線
        protected String contentCharSet ;
        protected String inputName = "inputStream";
        protected InputStream inputStream;
        protected int bufferSize = 1024;
        protected boolean allowCaching = true;

    當(dāng)然這些參數(shù)都可以在 <result  type="stream">中配置 例如;
    <param name="inputPath">\uploads\document.pdf</param>
    <!-- 指定下載文件的文件類型 -->
    <param name="contentType">Application/pdf</param>
    <!-- 指定由getTargetFile()方法返回被下載文件的InputStream -->
    <param name="inputName">targetFile</param>
    <param name="contentDisposition">attachment;filename="document.pdf"</param>
    <!-- 指定下載文件的緩沖大小 -->
    <param name="bufferSize">2048</param>
    其中:
        contentType:指定被下載文件的文件類型。 application/octet-stream 默認(rèn)值,可以下載所有類型
          inputName:指定被下載文件的入口輸入流, 和DownloadAction中的getInputStream()對應(yīng),主要是獲得實(shí)際資源文件
          contentDisposition:指定下載的文件名和顯示方式,一般和文件名一致,但是要注意中文件名保存時(shí)亂碼問題,解決辦法就是進(jìn)行編碼處理
    <!-- 指定由getTargetFile()方法返回被下載文件的InputStream -->
    <param name="inputName">targetFile</param>
    是下載的入口 我們不需要在我們的action里面配置targetFile變量 但需要getTargetFile方法,默認(rèn)需要getInputStream()方法 也就是:inputName參數(shù)的值就是入口方法去掉get前綴、首字母小寫的

    字符串

    我們的action里面的代碼如下:


     private String inputPath;//通過strtus獲取文件地址 也可以直接寫例如:String inputPath = ServletActionContext.getRequest().getRealPath("\uploads\document.pdf");
     
     public void setInputPath(String value)
     {
      inputPath = value;
     }
    public InputStream getTargetFile() throws Exception
     { 
      return ServletActionContext.getServletContext().getResourceAsStream(inputPath);
     }
    如果報(bào)以下錯(cuò)誤:
    Can not find a java.io.InputStream with the name [targetFile] in the invocation stack. Check the <param name="inputName"> tag specified for this action.

    實(shí)際問題是ServletActionContext.getServletContext().getResourceAsStream(inputPath);找不到資源,請檢查你的path是否正確。
    而關(guān)于下載實(shí)際struts做了什么呢?我們看一部分源代碼代碼就很明白了:

      HttpServletResponse oResponse = (HttpServletResponse) invocation.getInvocationContext().get(HTTP_RESPONSE);
     // Set the content type
    ...
     //Set the content length
    ...
     // Set the content-disposition
    ...
     // Set the cache control headers if neccessary
    ...
    // Get the outputstream
    //------------
                oOutput = oResponse.getOutputStream();

                if (LOG.isDebugEnabled()) {
                    LOG.debug("Streaming result [" + inputName + "] type=[" + contentType + "] length=[" + contentLength +
                        "] content-disposition=[" + contentDisposition + "] charset=[" + contentCharSet + "]");
                }

                // Copy input to output
                LOG.debug("Streaming to output buffer +++ START +++");
                byte[] oBuff = new byte[bufferSize];
                int iSize;
                while (-1 != (iSize = inputStream.read(oBuff))) {
                    oOutput.write(oBuff, 0, iSize);
                }
                LOG.debug("Streaming to output buffer +++ END +++");

                // Flush
                oOutput.flush();
            }
            finally {
                if (inputStream != null) inputStream.close();
                if (oOutput != null) oOutput.close();
            }
    //-----------
    很簡單,就像以前在servlet中一樣通過getOutputStream 和配置content type ,content-disposition,cache control,content length這些參數(shù)的來實(shí)現(xiàn)。

    這樣就很簡單的實(shí)現(xiàn)了下載功能。
    以上是自己工作之余寫的以下總結(jié),不對的地方希望大家指點(diǎn),謝謝,轉(zhuǎn)載,請說明地址


    評論

    # re: struts2 文件上傳和下載,以及部分源碼解析  回復(fù)  更多評論   

    2011-12-27 22:55 by 熱太熱讓他熱天
    熱太熱兒童熱天熱天熱天

    # re: struts2 文件上傳和下載,以及部分源碼解析  回復(fù)  更多評論   

    2012-07-13 08:25 by 33
    frsfrefgr

    # re: struts2 文件上傳和下載,以及部分源碼解析  回復(fù)  更多評論   

    2012-09-04 13:41 by 33
    @33
    fdfdfd

    # re:  回復(fù)  更多評論   

    2012-09-04 13:42 by 33
    kjkj

    # re: struts2 文件上傳和下載,以及部分源碼解析  回復(fù)  更多評論   

    2013-07-13 11:16 by @張小梅
    看看,沒試,不知道好用不
    主站蜘蛛池模板: 青青草原1769久久免费播放| 国产性生大片免费观看性| 免费视频专区一国产盗摄| 亚洲美女视频一区二区三区| 88av免费观看入口在线| 日韩亚洲一区二区三区| 日本高清不卡aⅴ免费网站| 亚洲精品中文字幕无码蜜桃| 丁香花在线视频观看免费 | 91青青国产在线观看免费| 亚洲av无码无在线观看红杏| 99爱视频99爱在线观看免费| 亚洲导航深夜福利| 嫩草视频在线免费观看| 在线观看亚洲免费视频| 久久亚洲国产精品五月天婷| 免费无码黄网站在线看| 亚洲精品一区二区三区四区乱码 | 免费激情网站国产高清第一页| 午夜亚洲国产成人不卡在线| 久久久久久久国产免费看| 亚洲va国产va天堂va久久| 亚洲免费在线视频播放| 亚洲色偷偷综合亚洲av78| 亚洲国产精品成人一区| 免费a级毛片无码a∨免费软件 | 久久亚洲国产成人亚| 日本视频一区在线观看免费| 亚洲人成色在线观看| 久久精品国产精品亚洲人人| 97公开免费视频| 久久亚洲精品成人无码| 亚洲成AV人片在线观看ww| 久久久高清免费视频| 免费很黄无遮挡的视频毛片| 亚洲综合成人网在线观看| 成人免费男女视频网站慢动作| 日韩在线观看免费| 亚洲国产午夜精品理论片| 亚洲色一色噜一噜噜噜| h视频在线免费看|