commons-fileupload框架源码解析(六)--ParameterParser

  1. commons-fileupload框架源码解析(一)--实例
  2. commons-fileupload框架源码解析(二)--HTTP
  3. commons-fileupload框架源码解析(三)--ParseRequest
  4. commons-fileupload框架源码解析(四)--FileItemIterator
  5. commons-fileupload框架源码解析(五)--MultipartStream
  6. commons-fileupload框架源码解析(六)--ParameterParser
  7. commons-fileupload框架源码解析(七)--FileCleaningTracker
  8. commons-fileupload框架源码解析(八)--DeferredFileOutputStream

前言

在解析主体文本的参数内容的消息头中,经常看到ParameterParser的身影,比如在FileUploadBase中。这个主要用于解析参数内容的消息头内容,将其封装成更加方便使用的Map对象。
在阅读本章博客时,建议先调试ParameterParser,阅读不懂方法,再通过方法名查找定位到本章的相关方法描述,辅助理解。ParameterParse是如何调用的,可以看FileUploadBase的源码。

源码

ParameterParser构造方法

 public ParameterParser() {
        super();
    }

ParameterParser就是一个普通的java类,并没有继承什么,也没有用到什么设计模式。而且每次解析参数内容消息头时,ParameterParser都会将参数内容的消息头以该类成员变量进行处理,所以,每次解析参数内容消息头都需要new出一个ParameterParser

parse(char[],int,int,separator)

ParameteParaser有很多parse重载方法,最后都会调用这个parse(char[],int,int,separator)上。

    public Map parse(
        final char[] charArray,//参数内容消息头字符数组,如:form-data; name="file"; filename="1.txt"
        int offset,//偏移量,一般为0,影响解析消息头字符数组的起始位置
        int length,//长度,一般为消息头字符数组长度,影响解析消息头字符数组的结束位置
        char separator) {//切分出name/values的符号

        if (charArray == null) {
            return new HashMap();
        }
        HashMap params = new HashMap();
        this.chars = charArray;
        this.pos = offset;
        this.len = length;

        String paramName = null;
        String paramValue = null;
        while (hasChar()) {//判断当前位置pos是否在0<=pos<=字符串长度范围内
            paramName = parseToken(new char[] {
                    '=', separator });
            paramValue = null;
            if (hasChar() && (charArray[pos] == '=')) {//判断当位置pos对应的chars元素是不是等于号
                pos++; // skip '=' 跳过等号
                paramValue = parseQuotedToken(new char[] {
                        separator });

                if (paramValue != null) {
                    try {
                        //这里只是对拿到的参数值进行Base64进行解码,一般情况下不会用到,MimeUtility.decodeText(String)会判断参数是否
                        //存在"=?"的标记,有的话,才会进行编码,没有的话,会原封不动的返回传过来的参数
                        paramValue = MimeUtility.decodeText(paramValue);
                    } catch (UnsupportedEncodingException e) {
                        // let's keep the original value in this case
                        //抛出解码异常,也会保持值paramValue原来的数据
                    }
                }
            }
            if (hasChar() && (charArray[pos] == separator)) {//判断当前位置pos对应的chars元素是不是参数separator的值
                pos++; // skip separator--是就跳过其位置
            }
            if ((paramName != null) && (paramName.length() > 0)) {//判断paramName的值是否有效
                if (this.lowerCaseNames) {
                    paramName = paramName.toLowerCase(Locale.ENGLISH);//转换成小写
                }

                params.put(paramName, paramValue);
            }
        }
        return params;
    }

算是一个控制层的方法,调用解析方法,再将解析处理的key/value加入到Map中。

hasChar

   private boolean hasChar() {
        return this.pos < this.len;//判断当前位置pos是否在0<=pos<=字符串长度范围内len中,
    }

通过当前位置是否在字符数组的长度范围内来确定是否还有元素

parseToken(final char[])

 private String parseToken(final char[] terminators) {
        char ch;
        i1 = pos;
        i2 = pos;
        while (hasChar()) {
            ch = chars[pos];
            if (isOneOf(ch, terminators)) {//判断是否当前位置pos的元素是否是参数terminator中的其中一个
                break;
            }
            i2++;
            pos++;
        }
        return getToken(false);
    }

注:token指的就是key或者value的值。
开始会对token起始位置i1和token结束位置i2赋上pos的值,然后从当前位置pos开始在字符数组chars中找到第一个符合参数terminators给定的字符数组的其中一个terminators元素的chars元素位置,,作为其token结束位置i2的值,从而获得了token起始位置i1和token结束位置i2的一个范围,再交给getToken(false)进行处理,拿到getToken(false)的结果进行返回

isOneOf

   private boolean isOneOf(char ch, final char[] charray) {
        boolean result = false;
        for (char element : charray) {
            if (ch == element) {
                result = true;
                break;
            }
        }
        return result;
    }

循环charray数组,逐个判断charray的元素是否与ch相同。

getToken(boolean)

 private String getToken(boolean quoted) {
        // Trim leading white spaces
        while ((i1 < i2) && (Character.isWhitespace(chars[i1]))) {//如果token的起始位置i1位置上的元素是空格,就通过++,从而忽略其空格
            i1++;
        }
        // Trim trailing white spaces
        while ((i2 > i1) && (Character.isWhitespace(chars[i2 - 1]))) {//如果token的结束位置i2位置上的元素是空格,就通过--,从而忽略其空格
            i2--;
        }
        // Strip away quotation marks if necessary
        if (quoted
            && ((i2 - i1) >= 2)
            && (chars[i1] == '"')
            && (chars[i2 - 1] == '"')) {//根据参数quated确定是否进行忽略双引号
            i1++;
            i2--;
        }
        String result = null;
        if (i2 > i1) {//判断token的结束位置i2是否大于token的起始位置i1
            result = new String(chars, i1, i2 - i1);
        }
        return result;
    }

忽略从token起始位置i1到token结束位置i2范围内的空格,并根据参数quoted来确定是否忽略从token起始位置i1到token结束位置i2范围内的双引号字符,再判断token起始位置i1是否小于token结束位置i2,如果小于,构建出从token起始位置i1到token结束位置i2的字符串并返回,否则返回null

parseQuotedToken(final char[] )

    private String parseQuotedToken(final char[] terminators) {
        char ch;
        i1 = pos;
        i2 = pos;
        boolean quoted = false;//双引号标记
        boolean charEscaped = false;//转义字符标记
        while (hasChar()) {
            ch = chars[pos];//当前位置pos对应的chars元素,即当前chars元素
            //如果没有双引号,且当前chars元素是属于分割字符数组terminators中的其中一个,就认为从token的起始位置i1到token的结束位置i2的范围内是个有效的值
            //则跳出循环,然后交个getToken(true)进行处理
            if (!quoted && isOneOf(ch, terminators)) {
                break;
            }
            if (!charEscaped && ch == '"') {//如果不是转义字符,但是当前chars元素是个双引号,切换双引号标记quoted
                quoted = !quoted;
            }
            charEscaped = (!charEscaped && ch == '\\');//如果是当前chars元素转义字符,切换转义字符标记标记
            i2++;
            pos++;

        }
        return getToken(true);//传入true,使其忽略掉双引号并返回的有效的token
    }

先对token的起始位置i1和token的结束位置i2赋上当前位置pos的值,然后通过循环确定token的结束位置i2的位置,在循环中,如果发现并没有双引号,且当前chars元素是属于分割字符数组terminators中的其中一个,就认为从token的起始位置i1到token的结束位置i2的范围内是个有效的token,当确定好起始位置i1和token的结束位置i2后,调用getToken(true)拿到其有效token进行返回,注意调用getToken(boolean)的这个方法,参数值是写死了true的

你可能感兴趣的:(commons-fileupload框架源码解析(六)--ParameterParser)