- commons-fileupload框架源码解析(一)--实例
- commons-fileupload框架源码解析(二)--HTTP
- commons-fileupload框架源码解析(三)--ParseRequest
- commons-fileupload框架源码解析(四)--FileItemIterator
- commons-fileupload框架源码解析(五)--MultipartStream
- commons-fileupload框架源码解析(六)--ParameterParser
- commons-fileupload框架源码解析(七)--FileCleaningTracker
- 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的