• Struts2龙之总结


     一、Struts2执行流程: 

      1.客户端初始化一个指向servlet容器(tomcat)的请求;

      2.这个请求经过一系列过滤器(Filter);

      3.接着StrutsPrepareAndExecuteFilter被调用,StrutsPrepareAndExecuteFilter询问ActionMapper来决定这个请求是否需要调用某个Action;

      4.如果ActionMapper决定需要调用某个Action,FilterDispatcher把请求的处理交给ActionProxy;

      5.ActionProxy通过Configuration Manager询问框架的配置文件,找到需要调用的Action类;

      6.ActionProxy调用invoke方法创建一个ActionInvocation的实例。

      7.递归调用,判断是否还有下一个拦截器,当没有了下一个拦截器,就执行Action的业务方法;

      8.一旦Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果。返回结果通常是(但不总是,也可能是另外的一个Action链;

      9.返回结果的时候依旧经过一系列拦截器。

       请求 ---- StrutsPrepareAndExecuteFilter 核心控制器 ----- Interceptors 拦截器(实现代码功能,只访问action时执行 ) ----- Action execuute --- 结果页面 Result 

     二、在web.xml 配置struts2 前端控制器 (Filter

      <filter>

       <filter-name>struts2</filter-name>

       <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>

      </filter>

      <filter-mapping>

       <filter-name>struts2</filter-name>

       <url-pattern>/*</url-pattern>

      </filter-mapping>

     三、struts2 常量配置 (键值对 properties

      struts2 默认常量 在 default.properties 中配置

      常用常量

      <constant name="struts.i18n.encoding" value="UTF-8"/>  ----- 相当于request.setCharacterEncoding("UTF-8"); 解决post请求乱码

       <constant name="struts.action.extension" value="action,,"/>  --- 访问struts2框架Action访问路径 扩展名 (要求)

      * struts.action.extension=action,, 默认以.action结尾扩展名 和 不写扩展名 都会分发给 Action

       <constant name="struts.serve.static.browserCache" value="false"/> false不缓存,true浏览器会缓存静态内容,产品环境设置true、开发环境设置false

      <constant name="struts.devMode" value="true" />  提供详细报错页面,修改struts.xml后不需要重启服务器 (要求)

       <action>name <package>namespace属性 共同决定 Action的访问路径 !!!!!!!!

       例如

       <package name="usermanager" namespace="/user" extends="struts-default">

         <action name="hello" class="cn.itcast.struts2.demo1.HelloAction">

       访问路径 /user/hello.action

       3) <action> 元素配置默认值

        <package> namespace 默认值 /

      <action> class 默认值 ActionSupport

      <result> name 默认值 success

      <action> method默认值execute

     4struts2 配置文件分离

    通过 <include file="struts-part1.xml"/> struts2 配置文件 拆分

    分离配置文件的原则:按模块进行分离,再统一组装到struts.xml  (<include>标签)

    五、 Action的访问

    HTTP请求 提交 Struts2 StrutsPrepareAndExecuteFilter 核心控制器 ------ 请求分发给不同Action

    1、 让请求能够访问Action  ----- Action书写方式 三种

    第一种 Action可以是 POJO  ((PlainOldJavaObjects)简单的Java对象) ---- 不需要继承任何父类,实现任何接口

    * struts2框架 读取struts.xml 获得 完整Action类名

    * obj = Class.forName("完整类名").newInstance();

        * Method m = Class.forName("完整类名").getMethod("execute");  m.invoke(obj); 通过反射 执行 execute方法

    第二种 编写Action 实现Action接口

    Action接口中,定义默认五种 逻辑视图名称

    public static final String SUCCESS = "success";  // 数据处理成功 (成功页面)

    public static final String NONE = "none";  // 页面不跳转  return null; 效果一样

    public static final String ERROR = "error";  // 数据处理发送错误 (错误页面)

    public static final String INPUT = "input"; // 用户输入数据有误,通常用于表单数据校验 (输入页面)

    public static final String LOGIN = "login"; // 主要权限认证 (登陆页面)

    * 五种逻辑视图,解决Action处理数据后,跳转页面

    第三种 编写Action  继承ActionSupport  (推荐)

       Action中使用 表单校验、错误信息设置、读取国际化信息 三个功能

      

    2Action中方法调用

        1) 在配置 <action> 元素时,没有指定method属性, 默认执行 Action类中 execute方法

       <action name="request1" class="baoming.demo3.RequestAction1" />

        2) 在<action> 元素内部 添加 method属性,指定执行Action中哪个方法

    <action name="regist" class="baoming.demo4.RegistAction" method="regist"/> 执行 RegistAction regist方法

      

    3) 使用通配符* ,简化struts.xml配置

    <a href="${pageContext.request.contextPath }/user/customer_add.action">添加客户</a>

    <a href="${pageContext.request.contextPath }/user/customer_del.action">删除客户</a>

    struts.xml

    <action name="customer_*" class="baoming.struts2.demo4.CustomerAction" method="{1}"></action>   ---  {1}就是第一个* 匹配内容

    Action 构造方法调用几次?是否为单例

            Action的生命周期?

    因为每次请求都会调用构造方法,说明它是多实例的,不存在线程并发访问时的冲突问题

    Struts1会不会存在线程安全问题?Struts1是单例的,会存在线程并发访问时的安全问题

    3、 动态方法调用 (零配置路线)

       访问Action中指定方法,不进行配置

       1) 在工程中使用 动态方法调用 ,必须保证 struts.enable.DynamicMethodInvocation = true 常量值为true

       2) 在action的访问路径 中 使用 "!方法名"

    页面

    <a href="${pageContext.request.contextPath }/user/product!add.action">添加商品</a>

    配置

    <action name="product" class="bao.struts2.demo4.ProductAction"></action>

    执行 ProductAction 中的 add方法

    六、在Action中使用Servlet API

    1、 在Action 中解耦合方式 间接访问 Servlet API  --------- 使用 ActionContext 对象

    struts2 Action API 已经与 Servlet API 解耦合 (没有依赖关系 )

    * Servlet API 常见操作 : 表单提交请求参数获取,向requestsessionapplication三个范围存取数据

    actionContext = ActionContext.getContext();

     actionContext.getParameters(); 获得所有请求参数Map集合

        

    actionContext.put("company", "数据"); / actionContext.get("company") request范围存取数据

    3) actionContext.getSession(); 获得session数据Map,对Session范围存取数据

      actionContext.getSession().put("key","value");

    4) actionContext.getApplication(); 获得ServletContext数据Map,对应用访问存取数据

      actionContext.getApplication().put("key","value");

        *本质分析是否存在线程安全问题? 不会有 答案原码:ActionContext  (使用ThreadLocal存储相关的值)

    3、 在Action中直接通过 ServletActionContext 获得Servlet API

    ServletActionContext.getRequest() : 获得request对象 (session

    ServletActionContext.getResponse() : 获得response 对象

    ServletActionContext.getServletContext() : 获得ServletContext对象

    * 静态方法没有线程问题,ThreadLocal

    七、 结果页面的配置

    Action处理请求后, 返回字符串(逻辑视图名), 需要在struts.xml 提供 <result>元素定义结果页面

    1、 局部结果页面 和 全局结果页面

    <action name="result" class="bao.struts2.demo6.ResultAction">

    <!-- 局部结果  当前Action使用 -->

    <result name="success">/demo6/result.jsp</result>

    </action>

    <global-results>

    <!-- 全局结果 当前包中 所有Action都可以用-->

    <result name="success">/demo6/result.jsp</result>

    </global-results>

    2、 结果页面跳转类型

    * struts-default.xml 定义了 一些结果页面类型

    * 使用默认type dispatcher 转发 (request.getRequestDispatcher.forward

    1)  dispatcher Action 转发给 JSP

    2)  chain Action调用另一个Action (同一次请求)

    <result name="success" type="chain">hello</result>  hello是一个Actionname

    3redirect Action重定向到 JSP

         <result name="success" type="redirect">hello.action</result>  hello是一个Actionname

    4redirectAction Action重定向到另一个Action

    <result name="success" type="redirectAction">hello</result>

    Action : 可以作为模型,也可以是控制器 

    属性驱动 模型驱动

    第一种 Action 本身作为model对象,通过成员setter封装 (属性驱动 )

    页面:

    用户名  <input type="text" name="username" /> <br/>

    Action

    public class RegistAction1 extends ActionSupport {

    private String username;

    public void setUsername(String username) {

    this.username = username;

    }

    }

      * struts2  Action 是多实例

     struts1 Action 是单例的

      

    第二种 :创建独立model对象,页面通过ognl表达式封装 (属性驱动)

    页面:

    用户名  <input type="text" name="user.username" /> <br/>  ----- 基于OGNL表达式的写法

    Action

    public class RegistAction2 extends ActionSupport {

    private User user;

    public void setUser(User user) {

    this.user = user;

    }

    public User getUser() {

    return user;

    }

    }

    问题: 谁来完成的参数封装

    <interceptor name="params" class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/>

    第三种 :使用ModelDriven接口,对请求数据进行封装 (模型驱动 ) ----- 主流

    页面:

    用户名  <input type="text" name="username" /> <br/>  

    Action

    public class RegistAction3 extends ActionSupport implements ModelDriven<User> {

    private User user = new User(); // 必须手动实例化

    public User getModel() {

    return user;

    }

    }

    * struts2 有很多围绕模型驱动的特性

    * <interceptor name="modelDriven" class="com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor"/> 为模型驱动提供了更多特性

    对比第二种、第三种 第三种只能在Action中指定一个model对象,第二种可以在Action中定义多个model对象

    <input type="text" name="user.username" />

    <input type="text" name="product.info" />

    属性驱动和模型驱动都是框架的拦截器来帮助实现的。

     数据类型转换问题

    1struts2 内部提供大量类型转换器,用来完成数据类型转换问题

    boolean Boolean

    charCharacter

    int Integer

    long Long

    float Float

    double Double

    Date 可以接收 yyyy-MM-dd格式字符串  (yyyy/MM/dd不能转)

    数组  可以将多个同名参数,转换到数组中

    集合  支持将数据保存到 List 或者 Map 集合

    案例: 输入合法年龄和生日可以自动转换

    当输入abc 转换为 int类型 age

    Caused by: java.lang.NoSuchMethodException: bao.struts2.demo3.CustomerAction.setAge([Ljava.lang.String;

    分析: 输入20 ,转换 int类型20  --- setAge(int)

          输入abc,转换int 出错 ---- setAge(String) ----- 报错方法不存在异常

    2、 自定义类型转换器 (了解

    1) 自定义类型转换器

    第一种 实现TypeConverter接口

    convertValue(java.util.Map<java.lang.String,java.lang.Object> context, java.lang.Object target, java.lang.reflect.Member member, java.lang.String propertyName, java.lang.Object value, java.lang.Class toType)

    第二种 继承 DefaultTypeConverter

    convertValue(java.util.Map<java.lang.String,java.lang.Object> context, java.lang.Object value, java.lang.Class toType)

    第三种 继承 StrutsTypeConverter

    convertFromString(java.util.Map context, java.lang.String[] values, java.lang.Class toClass)  --- 请求封装

    convertToString(java.util.Map context, java.lang.Object o)   --- 数据回显

     类型转换器 一直都是双向转换

    页面提交请求参数,封装到model --- 需要转换

    model数据 需要在页面 回显  ---- 需要转换

    2) 以 1990/10/10 为例,自定义日期转换器,完成转换  第二种方法

    public Object convertValue(Map<String, Object> context, Object value,

    Class toType) {

    // 根据toType判断 是请求封装 还是 数据回显

    DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");

    if (toType == Date.class) {

    // 请求参数封装 (value是字符串)

    String[] params = (String[]) value;

    String strVal = params[0]; // 转换为 日期类型

    try {

    return dateFormat.parse(strVal);

    } catch (ParseException e) {

    e.printStackTrace();

    }

    } else {

    // 回显(valueDate)

    Date date = (Date) value;

    return dateFormat.format(date);

    }

    return null;

    }

    3) 注册类型转换器

    局部注册 只对当前Action有效 (针对属性)

    全局注册 针对所有Action的日期类型有效 (针对类型 )

    局部注册 Action类所在包 创建 Action类名-conversion.properties , 格式 : 属性名称=类型转换器的全类名

    全局注册 src下创建 xwork-conversion.properties ,格式 : 待转换的类型=类型转换器的全类名

    3、 类型转换中错误处理

    通过分析拦截器作用,得知当类型转换出错时,自动跳转input视图 ,在input视图页面中 <s:fieldError/> 显示错误信息

    * Action所在包中,创建 ActionName.properties,在局部资源文件中配置提示信息 : invalid.fieldvalue.属性名= 错误信息

    校验器

    1、 校验的分类 : 客户端数据校验 和 服务器端数据校验

    客户端数据校验 ,通过JavaScript 完成校验 (改善用户体验,使用户减少出错 )

    服务器数据校验 ,使用框架内置校验功能 struts2 内置校验功能 ) ----- 必须的

    2struts2 支持校验方式

    代码校验 :在服务器端通过编写java代码,完成数据校验

    配置校验 XML配置校验(主流) 和 注解配置校验

    4XML配置方式 数据校验 (企业主流校验)

    代码校验 不适用于大型项目, 流程数据复杂时,开发量和维护量 都会很大

    xml配置校验原理 : 将很多校验规则代码已经写好,只需要在xml中定义数据所使用校验规则就可以了

    步骤一 :编写jsp

    步骤二 :编写Action 继承ActionSupport 或者 实现 Validateable 接口

    步骤三 :封装请求参数

    * 使用xml校验 必须提供get方法

    步骤四 :编写校验规则xml文件

    Action所在包 编写 Action类名-validation.xml Action所有业务方法进行校验

    引入DTD

    ------ xwork-core-2.3.7.jar xwork-validator-1.0.3.dtd

    <!DOCTYPE validators PUBLIC

       "-//Apache Struts//XWork Validator 1.0.3//EN"

       "http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd">

    内置校验器定义文件

    xwork-core-2.3.7.jar /com/opensymphony/xwork2/validator/validators/default.xml

    内建校验器

    * required (必填校验器,要求被校验的属性值不能为null)

    * requiredstring (必填字符串校验器,要求被校验的属性值不能为null,并且长度大于0,默认情况下会对字符串去前后空格)

    * stringlength (字符串长度校验器,要求被校验的属性值必须在指定的范围内,否则校验失败,minLength参数指定最小长度,maxLength参数指定最大长度,trim参数指定校验field之前是否去除字符串前后的空格)

    * regex (正则表达式校验器,检查被校验的属性值是否匹配一个正则表达式,expression参数指定正则表达式,caseSensitive参数指定进行正则表达式匹配时,是否区分大小写,默认值为true)

    * int(整数校验器,要求field的整数值必须在指定范围内,min指定最小值,max指定最大值)

    * double(双精度浮点数校验器,要求field的双精度浮点数必须在指定范围内,min指定最小值,max指定最大值)

    * fieldexpression (字段OGNL表达式校验器,要求field满足一个ognl表达式,expression参数指定ognl表达式,该逻辑表达式基于ValueStack进行求值,返回true时校验通过,否则不通过)

    * email(邮件地址校验器,要求如果被校验的属性值非空,则必须是合法的邮件地址)

    * url(网址校验器,要求如果被校验的属性值非空,则必须是合法的url地址)

    * date(日期校验器,要求field的日期值必须在指定范围内,min指定最小值,max指定最大值)

    案例

    required  必填校验器

    <field-validator type="required">

           <message>性别不能为空!</message>

    </field-validator>

    requiredstring  必填字符串校验器

    <field-validator type="requiredstring">

           <param name="trim">true</param>

           <message>用户名不能为空!</message>

    </field-validator>

    stringlength:字符串长度校验器

    <field-validator type="stringlength">

    <param name="maxLength">10</param>

    <param name="minLength">2</param>

    <param name="trim">true</param>

    <message><![CDATA[产品名称应在2-10个字符之间]]></message>

    </field-validator>

    int:整数校验器

    <field-validator type="int">

    <param name="min">1</param>

    <param name="max">150</param>

    <message>年龄必须在1-150之间</message>

    </field-validator>

    date: 日期校验器

    <field-validator type="date">

    <param name="min">1900-01-01</param>

    <param name="max">2050-02-21</param>

    <-- OGNL表达式在配置文件中的写法-->

    <message>生日必须在${min}${max}之间</message>

    </field-validator>

    url:  网络路径校验器

    <field-validator type="url">

    <message>数据的主页地址必须是一个有效网址</message>

    </field-validator>

    email:邮件地址校验器

    <field-validator type="email">

    <message>电子邮件地址无效</message>

    </field-validator>

    regex:正则表达式校验器

    <field-validator type="regex">

         <param name="expression"><![CDATA[^13d{9}$]]></param>

         <message>手机号格式不正确!</message>

    </field-validator>

    fieldexpression : 字段表达式校验

    <field-validator type="fieldexpression">

           <param name="expression"><![CDATA[(password==repassword)]]></param>

           <message>两次密码输入不一致</message>

    </field-validator>

    如何对指定的方法校验 ??? 格式  Action类名-ActionName(<action>元素name属性)-validation.xml

    例如 校验AddCustomerActionexecute方法  配置 <action name="addcustomer" ...method="regist"/>

            校验文件名字: AddCusotmerAction-addcustomer-validation.xml

     国际化信息显示

    1、 国际化原理 ? 什么是国际化 ?

    同一款软件 可以为不同用户,提供不同语言界面  ---- 国际化软件   internationalization  --i18n    本地化localization  l10n

    需要一个语言资源包(很多properties文件,每个properties文件 针对一个国家或者语言 ,通过java程序根据来访者国家语言,自动读取不同properties文件 )

    2、 资源包编写

    properties文件命名 :  基本名称_语言(小写)_国家(大写).properties

    例如

    messages_zh_CN.properties 中文中国

    messages_en_US.properties 英文美国

    4struts2 框架国际化配置

    第一种 全局国际化信息文件 (所有Action都可以使用 ) ------- 最常用

    * properties文件可以在任何包中

    * 需要在struts.xml 中配置全局信息文件位置

    struts.xml

    <constant name="struts.custom.i18n.resources" value="messages"></constant>   messages.properties src根目录

    <constant name="struts.custom.i18n.resources" value="bao.resources.messages"></constant>   messages.properties bao.resources

    国际化信息

    Action中使用  : this.getText("msg");

    jsp中使用  :<s:text name="msg" />

    在配置文件中(校验xml) : <message key="agemsg"></message>

     自定义拦截器

    拦截器 的使用 ,源自Spring AOP(面向切面编程)思想

    拦截器 采用 责任链 模式

    *  在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。

        *  责任链每一个节点,都可以继续调用下一个节点,也可以阻止流程继续执行

    struts2 中可以定义很多个拦截器,将多个拦截器按照特定顺序 组成拦截器栈 (顺序调用 栈中的每一个拦截器 )

    拦截器与拦截器栈的区别?

      拦截器:<interceptor>   拦截器栈:<interceptor-stack>

      从功能上看,拦截器栈本身就是一个功能更加强大的拦截器

      从结构上看,拦截器栈是由多个拦截器有序组织在一起

    配置一个拦截器:<interceptor>

    拦截器栈:<interceptor-stack>

        在栈中引入一个定义好的拦截器<interceptor-ref name=""/>

    定义默认的拦截器栈:<default-interceptor-ref name=""/>

    类的写法

      1struts2 所有拦截器 都必须实现 Interceptor 接口

      2AbstractInterceptor 类实现了 Interceptor 接口. 并为 init, destroy 提供了一个空白的实现

    所有实际开发中,自定义拦截器 只需要 继承 AbstractInterceptor类, 提供 intercept 方法实现

    3、 常用struts2 拦截器

    <interceptor-ref name="modelDriven"/> 模型驱动

    <interceptor-ref name="fileUpload"/> 文件上传

    <interceptor-ref name="params"> 参数解析封装

    <interceptor-ref name="conversionError"/> 类型转换错误

    <interceptor-ref name="validation"> 请求参数校验

    <interceptor-ref name="workflow"> 拦截跳转 input 视图

    OracleMySQL 作为应用数据库区别

    mysql存在数据库概念,在企业开发中,针对一个项目创建一个单独数据库,创建单独用户, 为用户授予数据库权限 ,

    oracle 一个数据库就是一个服务,在这个库中可以存在很多用户,每个用户有单独表空间 ,针对一个项目,只需要创建一个用户

    Struts2 框架 上传与下载

    文件上传

    1) 企业常用文件上传技术 : jspSmartUpload(主要应用 JSP model1 时代) 、 fileupload Apache commons项目中一个组件)、 Servlet3.0 集成文件上传 Part

    2) 文件上传  enctype="multipart/form-data"  MIME协议定义多部分请求体 (消息体)

    3) 上传页面编写

    存在 <input type="file" name="upload"/> 上传项,必须提供name属性

    表单提交方式 必须 post 提交

    表单编码类型 enctype="multipart/form-data"  

    4Struts2 对文件上传的支持

    提供 FileUpload 拦截器,用于解析 multipart/form-data 编码格式请求,解析上传文件的内容

    fileUpload拦截器 默认在 defaultStack 栈中, 默认会执行的

    Action需要对上传文件内容进行接收

    页面:

    <input type="file" name="upload" />

    Action :

    public class UploadAction extends ActionSupport {

    // 接收上传内容

    // <input type="file" name="upload" />

    private File upload; // 这里变量名 和 页面表单元素 name 属性一致

    private String uploadContentType;//MIME类型   image/jpeg  text/html

    private String uploadFileName;

    }

    * 格式 : 上传表单项name属性 + ContentType 、 上传表单项name属性 + FileName

    * 为三个对象 提供 setter 方法

    通过FileUtils 提供 copyFile 进行文件复制,将上传文件 保存到服务器端

    5) Struts2 上传文件过程中错误处理

       配置 input 视图 ,作为上传出错后 跳转页面

       在文件上传时,如果发生错误 fileUpload拦截器 会设置错误信息,workflow拦截器 跳转到 input 视图

       通过 struts.multipart.maxSize 常量设置文件上传总大小限制

          * struts.multipart.maxSize=2097152 默认上传文件总大小 2MB

          * 超过文件总大小,跳转input 视图, 通过 <s:actionError /> 回显错误信息

       struts.xml 设置上传总大小

           <constant name="struts.multipart.maxSize" value="20000000"></constant>

       设置上传文件总大小,对所有上传form有效,只想对当前form进行设置,可以设置fileUpload拦截器属性

         FileUpload 拦截器有 3 个属性可以设置.

    * maximumSize: 上传文件的最大长度(以字节为单位), 默认值为 2 MB

    * allowedTypes: 允许上传文件的类型, 各类型之间以逗号分隔  image/jpeg,text/html

    * allowedExtensions: 允许上传文件扩展名, 各扩展名之间以逗号分隔

       如果针对fileUpload 进行参数设置,当出错时,在页面通过 <s:fieldError /> 回显错误信息

       

     文件下载

    文件下载原理:

      1.一个流  输出流

      2.两个头  response.setHeader("Content-Type","application/octet-stream");

                response.setHeader("Content-Disposition","attachment;filename=1.jpg");

    1struts2 完成文件下载,通过 结果集类型 (Result Typestream 来完成的

     struts-default.xml 定义 <result-type name="stream" class="org.apache.struts2.dispatcher.StreamResult"/>

    2) 使用Stream结果集 完成文件下载

    文件下载原理: 服务器读取下载文件内容,通过Response响应流写回, 设置 ContentTypeContentDisposition 头信息

    public class StreamResult extends StrutsResultSupport {

    protected String contentType = "text/plain"; // contentType头信息  (下载文件对应 MIME协议规定类型 )

    * html --- text/html . txt--- text/plain

    protected String contentDisposition = "inline"; // ContentDisposition头信息 (下载文件打开方式 inline浏览器内部打开, attachment 以附件形式打开)

    protected String inputName = "inputStream";  // 需要Action中 提供 getInputStream 方法 返回 InputStream 提供下载文件 内容

    }

    Action 提供 InputStream  返回值 getInputStream 方法 ------- 指定下载文件流

    配置 stream 结果集 参数 <param name="contentType">${contentType}</param> ---- Action 中提供 getContentType

    *  ServletActionContext.getServletContext().getMimeType(filename);

    配置 stream 结果集 参数 <param name="contentDisposition">attachment;filename=${filename}</param> ---- Action 提供 getFilename

    * 下载附件名乱码问题 , IE和火狐 解决不同

    public String encodeDownloadFilename(String filename, String agent)

    throws IOException {

    if (agent.contains("Firefox")) { // 火狐浏览器

    filename = "=?UTF-8?B?"

    + new BASE64Encoder().encode(filename.getBytes("utf-8"))

    + "?=";

    } else { // IE及其他浏览器

    filename = URLEncoder.encode(filename, "utf-8");

    }

    return filename;

    }

  • 相关阅读:
    [原创]可动态显示圆形图像或圆形文字的AvatarImageView
    [原创]自定义view之:快速开发一款Material Design风格的dialog的开源项目MDDialog
    [原创]自定义BaseAcitivity的实现,统一activity的UI风格样式
    [原创]android自定义动画的一点感悟
    [原创]Java中的字符串比较,按照使用习惯进行比较
    [原创]android使用代码生成LayerDrawable的方法和注意事项
    [原创]android开源项目源码解析(一)----CircleImageView的源码解析
    [原创]自定义公历农历日期选择器
    自定义android RadioButton View,添加较为灵活的布局处理方式
    实现仿知乎的开场动画,图片zoomin的效果,实现原理,没加动效
  • 原文地址:https://www.cnblogs.com/MessiAndDream/p/5867064.html
Copyright © 2020-2023  润新知