Java____利用HSSF导出、导入excel文件
————————————HSSF、XSSF、SXSSF的比较——————————
HSSF是POI工程对Excel 97(-2007)文件操作的纯Java实现
XSSF是POI工程对Excel 2007 OOXML (.xlsx)文件操作的纯Java实现
从POI 3.8版本开始,提供了一种基于XSSF的低内存占用的API----SXSSF
SXSSF通过一个滑动窗口来限制访问Row的数量从而达到低内存占用的目录,XSSF可以访问所有行。旧的行数据不再出现在滑动窗口中并变得无法访问,与此同时写到磁盘上。
在自动刷新的模式下,可以指定窗口中访问Row的数量,从而在内存中保持一定数量的Row。当达到这一数量时,在窗口中产生新的Row数据,并将低索引的数据从窗口中移动到磁盘中。
或者,滑动窗口的行数可以设定成自动增长的。它可以根据需要周期的根据一次明确的flushRow(int keepRows)调用来进行修改。
注意:针对 SXSSF Beta 3.8下,会有临时文件产生,比如:
poi-sxssf-sheet4654655121378979321.xml
文件位置:java.io.tmpdir这个环境变量下的位置
Windows 7下是C:\Users\xxxxxAppData\Local\Temp
Linux下是 /var/tmp/
要根据实际情况,看是否删除这些临时文件
官方也提供了一些解决方式:
https://issues.apache.org/bugzilla/show_bug.cgi?id=53493
与XSSF的对比
在一个时间点上,只可以访问一定数量的数据
不再支持Sheet.clone()
不再支持公式的求值
—————————————HSSF项目操作实例————————————
——————————ExcelReader预备数据处理文件——————————
从上传的文件中读取数据并返回:
package xxxxximport java.io.File;
import java.io.FileInputStream;
import java.sql.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.poifs.filesystem.OfficeXmlFileException;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.util.ArrayUtil;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;/*** 操作Excel表格的功能类*/
public class ExcelReader {private Sheet sheet;private Row row;/*** 读取Excel表格表头的内容* @param InputStream* @return String 表头内容的数组*/public String[] readExcelTitle(File file) {String[] title = null;try {Workbook wb = null;try{wb = new HSSFWorkbook(new FileInputStream(file));}catch(OfficeXmlFileException e){wb = new XSSFWorkbook(new FileInputStream(file));}sheet = wb.getSheetAt(0);row = sheet.getRow(0);// 标题总列数int colNum = row.getPhysicalNumberOfCells();title = new String[colNum];for (int i = 0; i < colNum; i++) {Cell cell = row.getCell(i);title[i] = cell.getStringCellValue();}} catch (Exception e) {e.printStackTrace();}return title;}/*** 读取Excel所有的sheet页名字* @param InputStream* @return String sheet内容的数组*/public String[] readExcelSheetName(File file) {String[] sheetName = null;try {Workbook wb = null;try{wb = new HSSFWorkbook(new FileInputStream(file));}catch(OfficeXmlFileException e){wb = new XSSFWorkbook(new FileInputStream(file));}int maxSheet=wb.getNumberOfSheets();sheetName=new String[maxSheet];for (int i = 0; i < maxSheet; i++) {sheet=wb.getSheetAt(i);sheetName[i]=sheet.getSheetName();}} catch (Exception e) {e.printStackTrace();}return sheetName;}public static void main(String[] args) {ExcelReader excle=new ExcelReader();//Map<Integer, List<String>> tmp=excle.readExcelContent(new File("c:/test.xls"),74,31);Map<Integer, List<String>> tmp=excle.readExcelContent(new File("c:/2007.xlsx"),74,31);Object[] obj=tmp.keySet().toArray();Arrays.sort(obj);for (int i = 1; i < obj.length; i++) {System.out.println(tmp.get(obj[i]).toString());}}/*** 读取Excel数据内容* @param InputStream* @return Map 包含单元格数据内容的Map对象*/public Map<Integer, List<String>> readExcelContent(File file) {Map<Integer, List<String>> content = new HashMap<Integer, List<String>>();try {Workbook wb = null;try{wb = new HSSFWorkbook(new FileInputStream(file));}catch(OfficeXmlFileException e){wb = new XSSFWorkbook(new FileInputStream(file));}sheet = wb.getSheetAt(0);// 得到总行数int rowNum = sheet.getLastRowNum()+1;row = sheet.getRow(0);int colNum = row.getPhysicalNumberOfCells();// 正文内容应该从第二行开始,第一行为表头的标题for (int i = 1; i < rowNum; i++) {row = sheet.getRow(i);if(row==null){break;}List<String> list = new ArrayList<String>();for (int j = 0; j < colNum; j++) {Cell cell = row.getCell(j);if(cell != null) {switch (cell.getCellType()) { case HSSFCell.CELL_TYPE_NUMERIC: // 数字 if (HSSFDateUtil.isCellDateFormatted(cell)) { //如果是date类型则 ,获取该cell的date值 list.add(HSSFDateUtil.getJavaDate(cell.getNumericCellValue()).toString()); } else { // 纯数字 double d = cell.getNumericCellValue();if (d - (int) d < Double.MIN_VALUE) { // 是否为int型list.add(Integer.toString((int) d));} else { // 是否为double型list.add(Double.toString(cell.getNumericCellValue()));}}break; case HSSFCell.CELL_TYPE_STRING: // 字符串 list.add(cell.getStringCellValue() + ""); break; case HSSFCell.CELL_TYPE_BOOLEAN: // Boolean list.add(cell.getBooleanCellValue() + ""); break; case HSSFCell.CELL_TYPE_FORMULA: // 公式 list.add(cell.getCellFormula() + ""); break; case HSSFCell.CELL_TYPE_BLANK: // 空值 list.add(""); break; case HSSFCell.CELL_TYPE_ERROR: // 故障 list.add(""); break; default: list.add(""); break;}} else {list.add("");}}content.put(i, list);}} catch (Exception e) {e.printStackTrace();}return content;}/*** 读取Excel数据内容 限制取多少个Cell 从0开始算* * @param maxRow 想要获取excel的最大长度* @return Map 包含单元格数据内容的Map对象*/public Map<Integer, List<String>> readExcelContent(File file,int maxRow,int maxCell) {return this.readExcelContent(file, maxRow, maxCell, 1);}/*** 读取Excel数据内容 限制取多少个Cell 从0开始算* * @param maxRow 想要获取excel的最大长度* @return Map 包含单元格数据内容的Map对象*/public Map<Integer, List<String>> readExcelContent(File file,int maxRow,int maxCell,int startRow) {return this.readExcelContent(file, maxRow, maxCell, startRow, 0);}/*** * @param file 文件* @param maxRow 最大行 -1为不限制* @param maxCell 最大列 -1为不限制* @param startRow 开始行 * @param sheetIndex 第几个sheet页* @return*/public Map<Integer, List<String>> <span style="color:#ff0000;">readExcelContent</span>(File file,int maxRow,int maxCell,int startRow,int sheetIndex) {Map<Integer, List<String>> content = new HashMap<Integer, List<String>>();try {Workbook wb = null;try{wb = new HSSFWorkbook(new FileInputStream(file));}catch(OfficeXmlFileException e){wb = new XSSFWorkbook(new FileInputStream(file));}sheet = wb.getSheetAt(sheetIndex);// 得到总行数、总列数int rowNum = sheet.getLastRowNum()+1;row = sheet.getRow(0);int colNum = row.getPhysicalNumberOfCells();if(maxRow==-1){maxRow=rowNum;}// 正文内容应该从第二行开始,第一行为表头的标题for (int i = startRow; i < maxRow; i++) {row = sheet.getRow(i);if(row==null){break;}if(row.getZeroHeight()==true){System.out.println("row("+(i++)+")是隐藏的");continue;}List<String> list = new ArrayList<String>();for (int j = 0; j < colNum; j++) {if(maxCell!=-1&&j>maxCell){//如限制行数break;}Cell cell = row.getCell(j);if(cell != null) {boolean isMerge = isMergedRegion(sheet, i, cell.getColumnIndex()); if(isMerge) { String rs = getMergedRegionValue(sheet, row.getRowNum(), cell.getColumnIndex()); list.add(rs); }else{switch (cell.getCellType()) { case HSSFCell.CELL_TYPE_NUMERIC: // 数字 if (HSSFDateUtil.isCellDateFormatted(cell)) { //如果是date类型则 ,获取该cell的date值 list.add(HSSFDateUtil.getJavaDate(cell.getNumericCellValue()).toString()); } else { // 纯数字 double d = cell.getNumericCellValue();if (d - (int) d < Double.MIN_VALUE) { // 是否为int型list.add(Integer.toString((int) d));} else { // 是否为double型list.add(Double.toString(cell.getNumericCellValue()));}}break; case HSSFCell.CELL_TYPE_STRING: // 字符串 list.add(cell.getStringCellValue() + ""); break; case HSSFCell.CELL_TYPE_BOOLEAN: // Boolean list.add(cell.getBooleanCellValue() + ""); break; case HSSFCell.CELL_TYPE_FORMULA: // 公式 list.add(cell.getCellFormula() + ""); break; case HSSFCell.CELL_TYPE_BLANK: // 空值 list.add(""); break; case HSSFCell.CELL_TYPE_ERROR: // 故障 list.add(""); break; default: list.add(""); break;}}} else {list.add("");}}content.put(i, list);}} catch (Exception e) {e.printStackTrace();}return content;}/*** 获取单元格数据内容为字符串类型的数据* * @param cell Excel单元格* @return String 单元格数据内容*/private String getStringCellValue(HSSFCell cell) {String strCell = "";switch (cell.getCellType()) {case HSSFCell.CELL_TYPE_STRING:strCell = cell.getStringCellValue();break;case HSSFCell.CELL_TYPE_NUMERIC:strCell = String.valueOf(cell.getNumericCellValue());break;case HSSFCell.CELL_TYPE_BOOLEAN:strCell = String.valueOf(cell.getBooleanCellValue());break;case HSSFCell.CELL_TYPE_BLANK:strCell = "";break;default:strCell = "";break;}if (strCell.equals("") || strCell == null) {return "";}if (cell == null) {return "";}return strCell;}/*** 是否为单元格* @param sheet* @param row* @param column* @return*/private boolean isMergedRegion(Sheet sheet,int row ,int column) { int sheetMergeCount = sheet.getNumMergedRegions(); for (int i = 0; i < sheetMergeCount; i++) { CellRangeAddress range = sheet.getMergedRegion(i); int firstColumn = range.getFirstColumn(); int lastColumn = range.getLastColumn(); int firstRow = range.getFirstRow(); int lastRow = range.getLastRow(); if(row >= firstRow && row <= lastRow){ if(column >= firstColumn && column <= lastColumn){ return true; } } } return false; } /** * 获取合并单元格的值 * @param sheet * @param row * @param column * @return */ public String getMergedRegionValue(Sheet sheet ,int row , int column){ int sheetMergeCount = sheet.getNumMergedRegions(); for(int i = 0 ; i < sheetMergeCount ; i++){ CellRangeAddress ca = sheet.getMergedRegion(i); int firstColumn = ca.getFirstColumn(); int lastColumn = ca.getLastColumn(); int firstRow = ca.getFirstRow(); int lastRow = ca.getLastRow(); if(row >= firstRow && row <= lastRow){ if(column >= firstColumn && column <= lastColumn){ Row fRow = sheet.getRow(firstRow); Cell fCell = fRow.getCell(firstColumn); return getCellValue(fCell) ; } } } return null ; } /** * 获取单元格的值 * @param cell * @return */ public String getCellValue(Cell cell){ if(cell == null) return " "; if(cell.getCellType() == Cell.CELL_TYPE_STRING){ return cell.getStringCellValue(); }else if(cell.getCellType() == Cell.CELL_TYPE_BOOLEAN){ return String.valueOf(cell.getBooleanCellValue()); }else if(cell.getCellType() == Cell.CELL_TYPE_FORMULA){ return cell.getCellFormula() ; }else if(cell.getCellType() == Cell.CELL_TYPE_NUMERIC){ return String.valueOf(cell.getNumericCellValue()); } return " "; }
}
——————————ExcelReader数据处理文件——————————
对从表中读取的内容进行处理并进行储存:
public void uploadBosPlan() throws Exception{StringBuffer issues=new StringBuffer();this.ddsManage.deleteBosPlan();ExcelReader excle=new ExcelReader();File f=new File(filename);saveLatestExcel(f,"latestBosPlan.xls");//保存最近的中心线文件String[] sheetNames=excle.readExcelSheetName(f);for (int i = 0; i < sheetNames.length; i++) {Map<Integer, List<String>> contents=excle.readExcelContent(f,-1,-1,0,i);Object[] linenos=contents.keySet().toArray();//行数列表if(linenos==null||linenos.length==0){continue;}String sheetname=sheetNames[i];//sheet名字=//设置数据表关键字String[] tagInfo=new String[11];tagInfo[0] = "date";tagInfo[1] = "banzu_night1";tagInfo[2] = "banzu_night2";tagInfo[3] = "banzu_morning1";tagInfo[4] = "banzu_morning2";tagInfo[5] = "banzu_morning3";tagInfo[6] = "banzu_mid1";tagInfo[7] = "banzu_mid2";tagInfo[8] = "banci_night";tagInfo[9] = "banci_morning";tagInfo[10]= "banci_mid";CData planInfo=new CData();for (int k = 3; k < contents.size(); k++) {//从第4行有数据的位置开始遍历List<String> totalNames=contents.get(k);//获取第4行for(int j = 0;j<11;j++){//遍历每一列try {<span style="white-space:pre"> </span>//现将string类型转成date型 然后转成需要的类型if(j==0){SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd", java.util.Locale.US);SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd HH:mm:ss z yyyy", java.util.Locale.US);try{Date date = sdf.parse(totalNames.get(0));String dateString = formatter.format(date);data.set(tagInfo[0], dateString);}catch(Exception e){}}else{data.set(tagInfo[j],totalNames.get(j));}} catch (Exception e) {String errorMsg=sheetname+":"+totalNames.get(j)+"列出错";issues.append(errorMsg);LogAdder.PrintLog("TestLog.txt", errorMsg,e.getStackTrace());}}System.out.println(data);this.ddsManage.insertBosPlan(data);}}Struts2Utils.getResponse().getWriter().print(issues.toString());}
——————————ExportExcelOgc预处理文件——————————
对数据库中采集的数据进行处理并传入到Export处理程序中:
public String exportExcel() throws Exception{page = this.ddsManage.bosplanconfig(page, data);List<CData> tlist = page.getResult();String[] tagInfo=new String[11];tagInfo[0] = "date";tagInfo[1] = "banzu_night1";tagInfo[2] = "banzu_night2";tagInfo[3] = "banzu_morning1";tagInfo[4] = "banzu_morning2";tagInfo[5] = "banzu_morning3";tagInfo[6] = "banzu_mid1";tagInfo[7] = "banzu_mid2";tagInfo[8] = "banci_night";tagInfo[9] = "banci_morning";tagInfo[10]= "banci_mid";ExportexcelBosPlan export = new ExportexcelBosPlan();export.salesprotocol(tagInfo, tlist);return "bosplanconfig";}
——————————ExportExcelOgc处理文件——————————
对传来的数据进行处理并存储到Excel中:
package com.csValue.exportexcel;import java.io.OutputStream;
import java.io.PrintStream;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.util.Region;
import org.springside.modules.utils.web.struts2.Struts2Utils;
import ssdevframework.core.collection.CData;public class ExportexcelBosPlan
{public static String salesprotocol(String[] tContent, List<CData> tlist1 )throws Exception{HSSFWorkbook workbook = new HSSFWorkbook(); //一个新的excel表HSSFSheet sheet1 = workbook.createSheet("BOS_OGM计划表"); //创建sheet页 并命名HSSFCellStyle style = workbook.createCellStyle(); //创建新的stylestyle.setVerticalAlignment((short)1);style.setAlignment((short)2);HSSFCellStyle style1 = workbook.createCellStyle(); //创建新的style1style1.setVerticalAlignment((short)1);style1.setAlignment((short)2);HSSFCellStyle style2 = workbook.createCellStyle(); //创建新的style2style2.setVerticalAlignment((short)1);style2.setAlignment((short)2);//sheet1内容sheet1.addMergedRegion(new Region(0, (short)0, 0, (short)10)); // 合并的是第一行的0到length-1列HSSFRow row = sheet1.createRow(0); HSSFFont font = workbook.createFont(); //创建字体格式font.setFontName("黑体");font.setFontHeightInPoints((short)16);font.setBoldweight((short)700);style.setFont(font);HSSFCell cell = row.createCell((short)0); //从此处开始是对第一合并行赋值cell.setCellValue("全员BOS/OGM计划");cell.setCellStyle(style);sheet1.addMergedRegion(new Region(1, (short)0, 2, (short)0));sheet1.addMergedRegion(new Region(1, (short)1, 2, (short)2));sheet1.addMergedRegion(new Region(1, (short)3, 2, (short)5));sheet1.addMergedRegion(new Region(1, (short)6, 2, (short)7));sheet1.addMergedRegion(new Region(1, (short)8, 2, (short)10));HSSFFont font1 = workbook.createFont(); //创建字体格式font1.setBoldweight((short)700);row = sheet1.createRow(1); //第2行写入表头//第一列cell = row.createCell((short)0);cell.setCellType(1);cell.setCellValue("日期/班次");style2.setFont(font1);cell.setCellStyle(style2);cell = row.createCell((short)1);cell.setCellType(1);cell.setCellValue("夜班");style2.setFont(font1);cell.setCellStyle(style2);cell = row.createCell((short)3);cell.setCellType(1);cell.setCellValue("早班");style2.setFont(font1);cell.setCellStyle(style2);cell = row.createCell((short)6);cell.setCellType(1);cell.setCellValue("中班");style2.setFont(font1);cell.setCellStyle(style2);cell = row.createCell((short)8);cell.setCellType(1);cell.setCellValue("班次");style2.setFont(font1);cell.setCellStyle(style2);for (int i = 0; i < tlist1.size(); i++) { //第四行 遍历每班成员row = sheet1.createRow((short)(i + 3));for (int j = 0; j < tContent.length; j++) {fun(cell, i, j, tContent[j], row, style1, tlist1);}}try{System.out.println("BOS_OGM计划.xls");HttpServletResponse resp = Struts2Utils.getResponse();resp.setCharacterEncoding("UTF-8");resp.setContentType("text/html;charset=utf-8");resp.setContentType("application/x-download");resp.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("BOS_OGM计划.xls", "UTF-8"));OutputStream out = resp.getOutputStream();workbook.write(out);out.close();return "yes";} catch (Exception e) {e.printStackTrace();}return null;}public static void fun(HSSFCell cell, int k, int j, String str, HSSFRow row, HSSFCellStyle style, List<CData> list) {cell = row.createCell((short)j); //从一列开始遍历cell.setCellType(1);String cellvale = ((CData)list.get(k)).getString(str);if ((cellvale == null) || (cellvale.equals("null"))) {cellvale = "";}cell.setCellValue(cellvale);cell.setCellType(1);cell.setCellStyle(style);}
}
Java____利用HSSF导出、导入excel文件相关推荐
- JavaScript中如何导出/导入Excel文件?SpreadJS轻松搞定
SpreadJS v14.0正式版下载 JavaScript是一个涵盖多种框架.直译式.可以轻松自定义客户端的脚本语言,在 Web 应用程序中,更加易于编码和维护.而Excel 作为一款深受用户喜爱的 ...
- 如何使用 MySQL 的 IDE 导出导入数据表文件(以 Navicat for MySQL 导出导入 Excel 文件为例)
系列文章目录 关于更多 MySQL 数据库以及数据库 IDE 的问题大家可以移步本人专栏--MySQL 数据库. 文章目录 系列文章目录 前言 一.使用 Navicat 导出数据表 1.1.使用&qu ...
- php 利用 PHPExcel 导出 导入 Excel 方法介绍(功能介绍)
第一步 phpexcel官网上http://phpexcel.codeplex.com 下载最新的phpexcel类,下载解压缩后有一个classes文件夹,里面包含了PHPExcel.php和PHP ...
- php导入excel源码,利用PHPExcel类库,实现PHP导出导入Excel表格Excel文件!
[温馨提示]源码包解压密码:www.youhutong.com 利用PHPExcel类库,实现PHP导出导入Excel文件!(案例教程源码) 需要注意的地方就是: 1.导出文件时,如果你的字段过多,可 ...
- python将字典导入excel_python将字典列表导出为Excel文件的方法
将如下的字典列表内容导出为Excel表格文件形式: 关于上图字典列表的写入,请参考文章:https://www.jb51.net/article/169088.htm python将字典列表导出为Ex ...
- java导出为excel文件_java导出数据到excel文件
有的时候,将一些有用的数据导出到excel是很有必要的.比如说,我现在在做一个学校的在线教学平台,有一个需求是:将学生成绩导出到excel文件中去. 那怎样实现用java导出数据到excel文件呢?? ...
- ssh poi导出导入Excel
首先需要导入支持jar包 poi-3.11-20141221.jar poi-ooxml-3.11-20141221.jar poi-ooxml-schemas-3.11-20141221.jar x ...
- 在matlab中导入excel,Matlab导入Excel文件中的数据的详细教程分享
操作Matlab时还不会导入Excel文件中的数据?本文就介绍了Matlab导入Excel文件中的数据的操作内容,想要学习的朋友可以看一看下文哦,相信对大家会有所帮助的. 直接点击鼠标操作导入数据 打 ...
- node爬取app数据_node爬取拉勾网数据并导出为excel文件
前言 之前断断续续学习了node.js,今天就拿拉勾网练练手,顺便通过数据了解了解最近的招聘行情哈!node方面算是萌新一个吧,希望可以和大家共同学习和进步. 一.概要 我们首先需要明确具体的需求: ...
- js导出的xlsx无法打开_vue将数据导出为excel文件就是如此简单
前言: 在以前需要将数据导出为excel文件时,往往需要后端提供支持,后端导出后以文件流的形式进行下载.但也带了一些问题,如: 1. 代码量复杂,一般前端难以完成. 2. 实现数据导出往往会大量消耗服 ...
最新文章
- 中国互联网+激光加工行业商业模式创新与投资机会深度研究报告
- redis序列化_实例讲解Springboot以Template方式整合Redis及序列化问题
- 这几种思维模式,都是你的职业发展的绊脚石
- 数列分段pascal程序
- 变频器显示5cf1是什么意思_空调显示e0什么意思
- html-盒子模型及pading和margin相关
- linux su切换用户提示Authentication failture的解决办法
- 本地计算机所安装的网络组件,3.2 在本地计算机中安装和配置IIS
- Springmvc源码分析、底层原理
- python—将array格式图片保存至文件夹中
- 由摄像机的内参K计算视景体——glFrustum的参数推导
- linux ubuntu软件中心,Ubuntu 20.04 将Ubuntu软件中心切换到Snap商店
- 井下各种压力概念及相互关系
- Unity实用小工具或脚本——读写Json工具
- 【POJ C++题目】魔兽世界之一:备战
- BUG InvalidArgumentError (see above for traceback): You must feed a value for placeholder tensor 'Pl
- python二分法求最值_数值分析之二分法、试值法 python
- T156530 儒略历-传智杯-练习赛
- 1、pth转onnx模型、onnx转tensorrt模型、python中使用tensorrt进行加速推理(全网最全,不信你打我)
- Web页面动态验证码