使用万能表单解析程序将表单数据封装成List<Map>
在JadePool中,提供了一个 万能表单解析类ProcessForm,目的是提供对ProcessVO实现插入或更新记录的支持。 它能将任意复杂的表单记录,包括所有的字段,包括单记录和多记录,解析并封装成List<Map>变量,这些结果可以作为ProcessVO插入、更新等方法的参数保存到数据库中。保存记录时,ProcessVO会自动过滤掉无效字段,只有表单中的字段名与数据库表中的字段名相同的数据才会保存到数据库中。目前,ProcessForm可能是世界上最完整的表单解析工具。
ProcessForm支持所有格式的文件上传,但不支持大文件的上传,能满足大多数网络环境的应用。
对于文件类型的字段,除了会解析出文件的内容外,还会给出文件的相关属性,这些属性按照下面的格式命名:
文件字段名_originfilename
文件字段名_filename
文件字段名_ext
文件字段名_contenttype
文件字段名_size
对于四种常见的图片文件(jpg|gif|png|bmp),还会计算出图片宽度、高度等信息,
文件字段名_width
文件字段名_height
ProcessForm支持同步(非Ajax)和异步(Ajax)两种方式提交的表单的解析。
为了保证得到正确的解析结果,在表单设计上,使用这两个属性值:
method="post"和
enctype="multipart/form-data",
如果表单代表的是一条记录,对表单的字段顺序没有要求;如果表单代表的是多条记录,请不要把check、radio类型的字段作为每条记录的第一个字段,如果页面确实需要check、radio类型作为第一个字段,那么在check或radio之前加一个无效的隐含字段。ProcessVO不会保存无效字段值。
对于异步方式提交的表单数据,必须是Ajax标准格式,即xml格式。
ProcessForm应用:
同步上传的表单解析
ProcessForm form = ProcessForm.instance();
//form.setFilter("jpg|gif|png|swf");
//form.setAllowFileSize(1024000);//单个文件允许达到1M
form.parse(request);//同步解析
Map m = form.getRecordFullInfo();//包含文件的属性
m = form.getRecord()
List v = form.getRecordList();
v = form.getRecordFullInfoList();//包含文件的附件属性
异步上传的表单解析
ProcessForm pf = ProcessForm.instance();
pf.parseXML(request, pf.IS_ECMA_UNICODE);//表单字段的值均经过escape方法的处理
pf.parseXML(request);
List v = pf.getRecordList();
一点说明:由于表单解析程序的代码相当复杂,如果遇到不能解析的情况,请及时告诉本人。邮箱hkm2300(at)yahoo.com.cn
对同步方式提交的记录进行解析的部分底层核心方法
/** * 对非ajax方式提交的记录解析。 解析 contentType = "multipart/form-data";的表单 * 在表单中出现多条记录时,第一个元素作为新的一条记录的参考标志,必须确保其有一个固定的名称,所以表单中第一个元素应辟免使用checkbox复选框和radio单选按钮,以防止表的结构信息错误 */ private List getMultipartFormData(HttpServletRequest request) throws IOException, ServletException { Map recordFullInfo = null; List _recordFullInfoList = new ArrayList(); int recordIndex = -1; itemNameSet = new HashSet(); ServletInputStream in = request.getInputStream(); charset = request.getCharacterEncoding(); int allowFileSize = this.getAllowFileSize(); byte[] buffer = new byte[allowFileSize]; int bufferLength = 0; byte[] temp; byte[] lineByte = new byte[4096]; String line; String boundary = getBoundary(request); String itemName = ""; String originFileName = ""; String fileName = ""; String fileExt = ""; StringBuffer sb = new StringBuffer(""); boolean nameFlag = false; boolean fileFlag = false; int skipLine = 0; int i; try { while ((line = this.readLine(lineByte, in, charset)) != null) { if (!nameFlag && !fileFlag) { itemName = getName(line); if (itemName != null && !"".equals(itemName.trim())) { itemNameSet.add(itemName);//保存表单中的域名 if (recordIndex == -1) { firstItem = itemName; } if (firstItem.equals(itemName)) { recordIndex++; recordFullInfo = new HashMap();// _recordFullInfoList.add(recordIndex, recordFullInfo); } nameFlag = true; originFileName = this.getOriginFileName(line); fileName = getFileName(line); if (fileName != null && !"".equals(fileName)) { fileExt = getFileNameExt(fileName).toLowerCase(); if ("".equals(filter)) { ((Map) (_recordFullInfoList.get(recordIndex))).put(itemName + "_originfilename", originFileName); ((Map) (_recordFullInfoList.get(recordIndex))).put(itemName + "_filename", fileName); ((Map) (_recordFullInfoList.get(recordIndex))).put(itemName + "_ext", fileExt); } else { if (this.matchesFile(fileExt, filter)) { ((Map) (_recordFullInfoList.get(recordIndex))).put(itemName + "_originfilename", originFileName); ((Map) (_recordFullInfoList.get(recordIndex))).put(itemName + "_filename", fileName); ((Map) (_recordFullInfoList.get(recordIndex))).put(itemName + "_ext", fileExt); } else { throw new ServletException("Not matches file!"); } } fileFlag = true; } else { fileFlag = false; } skipLine = 0; continue; } continue; } if (nameFlag && !fileFlag) { if (skipLine < 1) { skipLine++; continue; } else { if (line.indexOf(boundary) >= 0) { nameFlag = false; //((Map) (_recordFullInfoList.get(recordIndex))).put(itemName, trim(sb.toString()));//保存普通字段名称和值 String s = sb.toString(); if (s.endsWith("\r\n")) { s = s.substring(0, s.length() - "\r\n".length());//去掉尾部的"\r\n" } if (s.endsWith("\r")) { s = s.substring(0, s.length() - "\r".length());//去掉尾部的"\r" } if (s.endsWith("\n")) { s = s.substring(0, s.length() - "\n".length());//去掉尾部的"\n" } ((Map) (_recordFullInfoList.get(recordIndex))).put(itemName, s);//在最新测试中,不需要转换\r\n,保存普通字段名称和值 sb = new StringBuffer(""); } else { sb.append(line); } } continue; } if (nameFlag && fileFlag) { if (skipLine < 1) { if (line.indexOf("Content-Type:") >= 0) { String s = line.substring(14);//"Content-Type: "的长度。 if (s.endsWith("\r\n")) { s = s.substring(0, s.length() - "\r\n".length());//去掉尾部的"\r\n" } if (s.endsWith("\r")) { s = s.substring(0, s.length() - "\r".length());//去掉尾部的"\r" } if (s.endsWith("\n")) { s = s.substring(0, s.length() - "\n".length());//去掉尾部的"\n" } //((Map) (_recordFullInfoList.get(recordIndex))).put(itemName + "_contenttype", trim(line.substring(14)));//"Content-Type: "的长度 ((Map) (_recordFullInfoList.get(recordIndex))).put(itemName + "_contenttype", s); } else { skipLine++; } continue; } else { //把文件保存到byte[]中 if (line.indexOf(boundary) >= 0) { if (bufferLength >= 2) { bufferLength -= 2; if (bufferLength < Integer.MAX_VALUE) { if (bufferLength < this.getAllowFileSize()) { temp = new byte[bufferLength]; for (i = 0; i < bufferLength; i++) { temp[i] = buffer[i]; } ((Map) (_recordFullInfoList.get(recordIndex))).put(itemName, temp);//将字段名和文件的字节数组保存到record中 ((Map) (_recordFullInfoList.get(recordIndex))).put(itemName + "_size", temp.length);//将文件的长度大小保存到record中 if (matchesFile(fileExt, "jpg|gif|png|bmp")) {//如果是图像文件,提取宽度和高度信息,并保存 ((Map) (_recordFullInfoList.get(recordIndex))).put(itemName + "_width", this.getIgmWidth(temp));//将字段名和文件的字节数组保存到record中 ((Map) (_recordFullInfoList.get(recordIndex))).put(itemName + "_height", this.getIgmHeight(temp));//将字段名和文件的字节数组保存到record中 } buffer = new byte[allowFileSize]; bufferLength = 0; } else { throw new ArrayIndexOutOfBoundsException("java.lang.ArrayIndexOutOfBoundsException: the file is bigger than allowFileSize[" + getAllowFileSize() + "] "); } } else { throw new ArrayIndexOutOfBoundsException("java.lang.ArrayIndexOutOfBoundsException: the file is too big. the max allow is " + Integer.MAX_VALUE); } } nameFlag = false; fileFlag = false; } else { for (i = 0; i < LINE_LENGTH; i++, bufferLength++) { buffer[bufferLength] = lineByte[i];//将字节数据写入 } } } } } } finally { in.close(); } return _recordFullInfoList; }
对异步上传提交的记录进行解析的部分底层核心方法
/** * 解析用xml格式保存的记录,如使用Ajax上传的xml代码 在xml代码中出现多条记录时,第一个元素作为新的一条记录的参考标志 */ private List getListFromXml(String xmlFragment) throws ServletException { indexXmlList = -1; xmlRecordList = new ArrayList(); itemNameSet = new HashSet(); Document doc = null; try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); doc = factory.newDocumentBuilder().parse(new InputSource(new StringReader(xmlFragment))); } catch (Exception e) { e.printStackTrace(); } List list = visitNode(doc); return list; } /** * 解析用xml格式保存的记录,如使用Ajax上传的xml代码 * 该记录的值经由escape(form.XXX.value)方法转换成JavaScript中的Unicode编码格式 * 在xml代码中出现多条记录时,第一个元素作为新的一条记录的参考标志 */ private List getListFromXml(String xmlFragment, boolean isEcmaUnicode) throws ServletException, UnsupportedEncodingException { indexXmlList = -1; xmlRecordList = new ArrayList(); itemNameSet = new HashSet(); Document doc = null; try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); doc = factory.newDocumentBuilder().parse(new InputSource(new StringReader(xmlFragment))); } catch (Exception e) { e.printStackTrace(); } List list = visitNode(doc, isEcmaUnicode); return list; } private List visitNode(Node node) throws ServletException { if (node.getNodeType() == 1) { Node text = node.getFirstChild(); if (text != null) { if ("#text".equals(text.getNodeName())) { if (indexXmlList == -1) { firstXmlTagName = node.getNodeName(); itemNameSet.add(firstXmlTagName); indexXmlList++; } if (firstXmlTagName.equals(node.getNodeName())) { xmlRecord = new HashMap(); firstXmlTagParentNode = node.getParentNode(); xmlRecordList.add(xmlRecord); indexXmlList++; } //如果当前的Element与第一个Element(字段)的父结点相同,则可以断定当前的Element肯定是字段 if ((indexXmlList > -1) && firstXmlTagParentNode.equals(node.getParentNode())) { itemNameSet.add(node.getNodeName()); ((Map) xmlRecordList.get(indexXmlList - 1)).put(node.getNodeName(), text.getNodeValue()); } } } else { //叶子结点肯定是字段,但这是一个空值字段 if (indexXmlList == -1) { //throw new ServletException("The first element '<" + node.getNodeName() + ">' could not be null!");//禁止第一个字段为空值 } if (indexXmlList == -1) { //允许第一个字段为空值 firstXmlTagName = node.getNodeName(); itemNameSet.add(firstXmlTagName); indexXmlList++; } if (firstXmlTagName.equals(node.getNodeName())) { xmlRecord = new HashMap(); firstXmlTagParentNode = node.getParentNode(); xmlRecordList.add(xmlRecord); indexXmlList++; } //如果当前的Element与第一个Element(字段)的父结点相同,则可以断定当前的Element的值为null if ((indexXmlList > -1) && firstXmlTagParentNode.equals(node.getParentNode())) { itemNameSet.add(node.getNodeName()); ((Map) xmlRecordList.get(indexXmlList - 1)).put(node.getNodeName(), "");//用""替代null,还原表单的本义,如果元素存在,request.getParameter('元素名')则肯定会得到字符串,其长度大于等于0;如果元素不存在,request.getParameter('元素名')则肯定得到null } } } NodeList nodeList = node.getChildNodes(); if (nodeList != null && nodeList.getLength() > 0) { for (int i = 0; i < nodeList.getLength(); i++) { Node childNode = nodeList.item(i); visitNode(childNode); } } return xmlRecordList; } /** * 解析ajax方式上传的xml格式的记录集。 为xml应用修改 * 在ajax应用中如json,pvo中的Map对象,Array中元素,它们的值均不能含有回车符和换行符,因此 \r 将转换成"" \n 将转换成"" * 此方法可以作为默认的xml解析方法,不论用户上传的数据是否作了escape转换,都能得到希望的结果 */ private List visitNode(Node node, boolean isEcmaUnicode) throws ServletException, UnsupportedEncodingException { if (node.getNodeType() == 1) { //System.out.println("isEcmaUnicode 2 is : " + isEcmaUnicode); Node text = node.getFirstChild(); String value = ""; if (text != null) { StringBuilder sb = new StringBuilder(); if ("#text".equals(text.getNodeName())) { if (indexXmlList == -1) { firstXmlTagName = node.getNodeName(); itemNameSet.add(firstXmlTagName); indexXmlList++; } if (firstXmlTagName.equals(node.getNodeName())) { xmlRecord = new HashMap(); firstXmlTagParentNode = node.getParentNode(); xmlRecordList.add(xmlRecord); indexXmlList++; } //如果当前的Element与第一个Element(字段)的父结点相同,则可以断定当前的Element的值为null if ((indexXmlList > -1) && firstXmlTagParentNode.equals(node.getParentNode())) { itemNameSet.add(node.getNodeName()); value = text.getNodeValue(); String oneChar = "";//用于从value中逐个提取字符 if (isEcmaUnicode && (value != null)) { while (value.length() > 0) { if (!value.startsWith("%")) { oneChar = value.substring(0, 1); if ("\n".equals(oneChar)) { //oneChar = ""; oneChar = "#pvo_n#";//替换 } if ("\r".equals(oneChar)) { //oneChar = ""; oneChar = "#pvo_r#";//替换 } sb.append(oneChar); value = value.substring(1); } else if (value.startsWith("%")) { if (value.startsWith("%u")) { oneChar = value.substring(2, 6); oneChar = ByteProcess.UnicodeHexCharsToString(oneChar); if ("\n".equals(oneChar)) { //oneChar = ""; oneChar = "#pvo_n#";//替换 } if ("\r".equals(oneChar)) { //oneChar = ""; oneChar = "#pvo_r#";//替换 } sb.append(oneChar); value = value.substring(6); } else { oneChar = "00" + value.substring(1, 3); oneChar = ByteProcess.UnicodeHexCharsToString(oneChar); if ("\n".equals(oneChar)) { //oneChar = ""; oneChar = "#pvo_n#";//替换 } if ("\r".equals(oneChar)) { //oneChar = ""; oneChar = "#pvo_r#";//替换 } sb.append(oneChar); value = value.substring(3); } } } } String s = sb.toString(); if (s.endsWith("#pvo_r##pvo_n#")) { s = s.substring(0, s.length() - "#pvo_r##pvo_n#".length());//去掉尾部的"#pvo_r##pvo_n#" } if (s.endsWith("#pvo_r#")) { s = s.substring(0, s.length() - "#pvo_r#".length());//去掉尾部的"#pvo_r#" } if (s.endsWith("#pvo_n#")) { s = s.substring(0, s.length() - "#pvo_n#".length());//去掉尾部的"#pvo_n#" } s = this.replace(s, "#pvo_r##pvo_n#", "\r\n");//还原 s = this.replace(s, "#pvo_n#", "\n");//还原 s = this.replace(s, "#pvo_r#", "\r");//还原 ((Map) xmlRecordList.get(indexXmlList - 1)).put(node.getNodeName(), s); } } } else { //叶子结点肯定是字段,但这是一个空值字段 if (indexXmlList == -1) { //throw new ServletException("The first element '<" + node.getNodeName() + ">' could not be null!");//禁止第一个字段为空值 } if (indexXmlList == -1) { //允许第一个字段为空值 firstXmlTagName = node.getNodeName(); itemNameSet.add(firstXmlTagName); indexXmlList++; } if (firstXmlTagName.equals(node.getNodeName())) { xmlRecord = new HashMap(); firstXmlTagParentNode = node.getParentNode(); xmlRecordList.add(xmlRecord); indexXmlList++; } //如果当前的Element与第一个Element(字段)的父结点相同,则可以断定当前的Element的值为null if ((indexXmlList > -1) && firstXmlTagParentNode.equals(node.getParentNode())) { itemNameSet.add(node.getNodeName()); ((Map) xmlRecordList.get(indexXmlList - 1)).put(node.getNodeName(), "");//用""替代null,还原表单的本义,如果元素存在,request.getParameter('元素名')则肯定会得到字符串,其长度大于等于0;如果元素不存在,request.getParameter('元素名')则肯定得到null } } } NodeList nodeList = node.getChildNodes(); if (nodeList != null && nodeList.getLength() > 0) { for (int i = 0; i < nodeList.getLength(); i++) { Node childNode = nodeList.item(i); visitNode(childNode, isEcmaUnicode); } } return xmlRecordList; }