• struts2 文件上传和下载,以及部分源代码解析


    struts2 文件上传 和部分源代码解析,以及一般上传原理

    (1) 单文件上传

    一.简单介绍

    Struts2并未提供自己的请求解析器,也就是就Struts2不会自己去处理multipart/form-data的请求,它须要调用其它请求解析器,将HTTP请求中的表单域解析出来。

    但Struts2在原有的上传解析器基础

    上做了进一步封装,更进一步简化了文件上传。
    Struts2默认使用的是Jakarta的Common-FileUpload框架来上传文件,因此,要在web应用中添加两个Jar文件:commons-fileupload-1.2.jar和commons-io-1.3.1.jar。它在原上传框架上做了进一步封装

    ,简化了文件上传的代码实现,取消了不同上传框架上的编程差异。
    假设要改成其他的文件上传框架,能够改动struts.multipart.parser常量的值为cos/pell,默认值是jakata。并在classpath中添加对应上传组件的类库

    比如配置成cos上传

    struts.multipart.parser=cos

    struts.multipart.maxSize=1024 指定文件的最大字结数

    二.原理

    无论用common-fileUPload框架,还是用cos,都是通过将HTTP的数据保存到暂时目录。然后Struts使用fileUpload拦截器将文件绑定到Action的实例中。


    也就是配置文件的
    我们能够通过源码struts2-code-XX.jar的struts-default.xml文件找到

    打开这个类的源码能够看见相关例如以下:
    /**

    *

    * 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:
    *

    *

      *

    *

    • [File Name] : File - the actual File


    *

    *

    • [File Name]ContentType : String - the content type of the file


    *

    *

    • [File Name]FileName : String - the actual name of the file uploaded (not the HTML name)


    *

    *

    *

    也就是说我们须要三个变量File(表单的name)。其它两个參数通过set个体方法有strtus调用

    接着以下是一些国际化提示的东西:
    * processed for all i18n requests. You can override the text of these messages by providing text for the following
    * keys:
    *

    • struts.messages.error.uploading - a general error that occurs when the file could not be uploaded


    *

    *

    • struts.messages.error.file.too.large - occurs when the uploaded file is too large


    *

    *

    • struts.messages.error.content.type.not.allowed - occurs when the uploaded file does not match the expected
      * content types specified


    *

    *

    • struts.messages.error.file.extension.not.allowed - occurs when the uploaded file does not match the expected
      * file extensions specified


    *

    比如struts.messages.error.content.type.not.allowed 表示文件类型错误:也就是说假设我们给拦截器配置了属性allowedTypes 比如:
    image/bmp,image/png,image/gif,image/jpeg,image/jpg 可是上传的时候没有上传规定的类型
    struts2就会去我们的资源文件去找key为struts.messages.error.content.type.not.allowed的国际化资源给与提示这时候我们能够在我们的资源中配置这个key:
    比如:struts.messages.error.content.type.not.allowed=您上传的文件类型仅仅能为...。请又一次选择!
    (当然须要)globalMessages为资源前缀,然后通过:来显示提示

    *

    *

      *

    *

    • maximumSize (optional) - the maximum size (in bytes) that the interceptor will allow a file reference to be set
      * on the action. Note, this is not related to the various properties found in struts.properties.
      * Default to approximately 2MB.


    *

    *

    • 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.


    *

    *

    • 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.


    *

    *

    上面则是拦截器的相关參数,一目了然:maximumSize 上传文件最大多少 默认:2MB。allowedTypes容许的上传类型。allowedExtensions容许的扩展名
    接着是相关action的代码说明:

    * package com.example;
    *

    * import java.io.File;
    * import com.opensymphony.xwork2.ActionSupport;
    *

    * public UploadAction extends ActionSupport {
    * private File file;
    * private String contentType;
    * private String filename;
    *

    * public void setUpload(File file) {
    * this.file = file;
    * }
    *

    * public void setUploadContentType(String contentType) {
    * this.contentType = contentType;
    * }
    *

    * public void setUploadFileName(String filename) {
    * this.filename = filename;
    * }
    *

    * public String execute() {
    * //...
    * return SUCCESS;
    * }
    * }
    事实上最基本的是set方法的确定:我们跟踪到大约238行:
    String contentTypeName = inputName + "ContentType";
    String fileNameName = inputName + "FileName";
    终于确定我们的private File file;属性名称能够随便,
    可是filenam和contenttype的set方法要有规定 比如:
    假设private File myFile;

    则相应的其它的两个属性set方法例如以下:

    public void setMyFileContentType(String contentType) {
    this.contentType = contentType;//当然contentType能够随便起名 终于要的是set+MyFile+ContentType方法
    }

    public void setMyFileFileName(String filename) {
    this.filename = filename;/当然filename能够随便起名 终于要的是set+MyFile+FileName方法
    }
    下面是实例:
    三.须要的jar包(默认使用commons-fileupload,假设使用cos。要将jar引进来)

    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

    四.实例

    1.首先,创建上传页面

    Html代码
    1.
    2.
    3.
    4.
    5.
    6.
    19.
    20.
    21.
    22. 
    23. 
    24. 
    25. 
    26. 

    27.
    28.
    29.

    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。与一般的上传一样.

    会将upload绑定到action的upload,其次他还会将上传记文件的MIME类型绑定到uploadContentType,文件名称绑定到uploadFileName中。他们是通过

    setUploadContentType和setUploadFileName进行绑定的,以下进行的多文件上传也是同个道理,只是要用数组或者是list来进行绑定。然后多个文件的MIME类型也会绑定到以数组

    名字加ContentType和FileName的字符串数组中。 比方说上传的文件的数组名为:File[] uploads,则它们的MIME类型绑定的相应的数组是uploadsFileName和uploadsContentType.

    3.struts.xml的配置

    Xml代码

    Xml代码
    1.
    2.
    5.
    6.
    7.
    8.
    9.
    10.
    11.
    12. image/bmp,image/png,image/gif,image/jpeg,image/jpg
    13.
    14.
    15.
    16. /fileUpload.jsp
    17. /showUpload.jsp
    18.
    19.
    20.

    4.最后是web.xml的配置

    Xml代码
    1.
    2.
    7.
    8.
    9. struts-cleanup
    10.
    11. org.apache.struts2.dispatcher.ActionContextCleanUp
    12.
    13.
    14.
    15.
    16. struts2
    17.
    18. org.apache.struts2.dispatcher.FilterDispatcher
    19.
    20.
    21.
    22.
    23. struts-cleanup
    24. /*
    25.
    26.
    27.
    28. struts2
    29. /*
    30.
    31.
    32.
    33. index.jsp
    34.
    35.

    (2) 多文件上传

    多文件上传
    与单文件上传相似,实现多文件你能够将多个绑定Action的数组或列表。

    例如以下例所看到的。






    清单14 多文件上传JSP代码片段
    假设你希望绑定到数组,Action的代码应类似:

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

    多文件上传数组绑定Action代码片段
    假设你想绑定到列表,则应类似:

    private List uploads ;
    private List uploadSFileName ;
    private List uploadSContentType ;
    多文件上传列表绑定Action代码片段

    另外是一般上传文件的原理:当然详细能够看http协议的rfc文档:
    关于multipart/form-data 相关资料能够看;http://www.ietf.org/rfc/rfc1867.txt 大约在[Page 1]的地方有介绍
    表单配置multipart/form-data 说明以二进制流的方式传输表单字段的数据:
    我们通过下面代码看到request数据流中的内容:

    PrintWriter out = response.getWriter();
    InputStream is = request.getInputStream();

    BufferedReader br = new BufferedReader(
    new InputStreamReader(is));
    String buffer = null;
    while( (buffer = br.readLine()) != null)
    {
    //在页面中显示读取到的请求參数
    out.println(buffer + "
    ");
    }
    out.flush();
    out.close();
    比如:我上传一个文件D:apache-tomcat-6018inversion.sh (tomcat版本号文件)
    终于页面显示:
    -----------------------------7da1052ec05fe
    Content-Disposition: form-data; name="ff"; filename="D:apache-tomcat-6018inversion.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--
    我们发现我们上传的内容在
    -----------------------------7da1052ec05fe
    Content-Disposition: form-data; name="ff"; filename="D:apache-tomcat-6018inversion.sh"
    Content-Type: text/plain和-----------------------------7da1052ec05fe--中间
    因此我们能够通过下面代码来获取上传内容并保存:

    //取得HttpServletRequest的InputStream输入流
    InputStream is = request.getInputStream();
    BufferedReader br = new BufferedReader(new InputStreamReader(is));
    String buffer = null;
    //循环读取请求内容的每一行内容
    while( (buffer = br.readLine()) != null)
    {
    //假设读到的内容以-----------------------------開始。
    //且以--结束,表明已到请求内容尾
    if(buffer.endsWith("--") && buffer
    .startsWith("-----------------------------"))//length为29
    {
    //跳出循环
    break;
    }
    //假设读到的内容以-----------------------------開始,表明開始了一个表单域
    if(buffer.startsWith("-----------------------------"))
    {
    //假设下一行内容中有filename字符串。表明这是一个文件域
    if (br.readLine().indexOf("filename") > 1)
    {
    //跳过两行,開始处理上传的文件内容
    br.readLine();
    br.readLine();
    //以系统时间为文件名称,创建一个新文件
    File file = new File(request.getRealPath("/")
    + System.currentTimeMillis());
    //当然我们能够读取filenam来保存这里简化
    //创建一个文件输出流
    PrintStream ps = new PrintStream(new FileOutputStream(file));
    String content = null;
    //接着開始读取文件内容
    while( (content = br.readLine()) != null)
    {
    //假设读取的内容以-----------------------------開始。
    //表明開始了下一个表单域内容
    if(content.startsWith("-----------------------------"))length为29
    {
    //跳出处理
    break;
    }
    //将读到的内容输出到文件里
    ps.println(content);
    }
    //关闭输出
    ps.flush();
    ps.close();
    }
    }
    }
    br.close();

    关于strtus2下载:
    下载终于是通过contentType和数据流将数据输出到client来实现。在struts中也是通过InputStream和相关的配置来实现:
    相同终于到strtus的下载相关的源码: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;

    当然这些參数都能够在 中配置 比如;
    uploadsdocument.pdf

    Application/pdf

    targetFile
    attachment;filename="document.pdf"

    2048
    当中:
    contentType:指定被下载文件的文件类型。 application/octet-stream 默认值,能够下载全部类型
    inputName:指定被下载文件的入口输入流, 和DownloadAction中的getInputStream()相应,主要是获得实际资源文件
    contentDisposition:指定下载的文件名称和显示方式,一般和文件名称一致,可是要注意中文件名称保存时乱码问题,解决的方法就是进行编码处理

    targetFile
    是下载的入口 我们不须要在我们的action里面配置targetFile变量 但须要getTargetFile方法。默认须要getInputStream()方法 也就是:inputName參数的值就是入口方法去掉get前缀、首字母小写的

    字符串

    我们的action里面的代码例如以下:

    private String inputPath;//通过strtus获取文件地址 也能够直接写比如:String inputPath = ServletActionContext.getRequest().getRealPath("uploadsdocument.pdf");

    public void setInputPath(String value)
    {
    inputPath = value;
    }
    public InputStream getTargetFile() throws Exception
    {
    return ServletActionContext.getServletContext().getResourceAsStream(inputPath);
    }
    假设报下面错误:
    Can not find a java.io.InputStream with the name [targetFile] in the invocation stack. Check the tag specified for this action.

    实际问题是ServletActionContext.getServletContext().getResourceAsStream(inputPath);找不到资源,请检查你的path是否正确。


    而关于下载实际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();
    }
    //-----------
    非常easy。就像曾经在servlet中一样通过getOutputStream 和配置content type ,content-disposition,cache control。content length这些參数的来实现。

    这样就非常easy的实现了下载功能。
    以上是自己工作之余写的下面总结,不正确的地方希望大家指点,谢谢,转载。请说明地址
    很多其它详情

  • 相关阅读:
    时间的计算
    中英文词频统计
    组合数据类型,英文词频统计
    作业二
    学号提取
    用for循环产生的网址
    温度转换
    身份证号码提取
    数字游戏
    更改mysql连接个数
  • 原文地址:https://www.cnblogs.com/wzjhoutai/p/6795615.html
Copyright © 2020-2023  润新知