需求:在实际J2EE项目中,经常遇到需要导入导出excel文件的情况;
解决方案: 利用java的annotation,自定义一个annotation ,在声明pojo的时候对需要操作字段的属性进行声明,
然后通用的处理类运行时,读取annotation 相关信息进行解析。
对于导入,读入是一个excel文件,输出一个list
对于导出,你需要组织一个List
1、页面效果如下图所示:
2、自定义的Exel的annotation如下图所示:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Excel {
//导入时,对应数据库的字段 主要是用户区分每个字段,不能有annocation重名的
//导出时的列名 导出排序跟定义了annotation的字段的顺序有关
public String exportName();
//导出时在excel中每个列的宽 单位为字符,一个汉字=2个字符
//如 以列名列内容中较合适的长度 例如姓名列6 【姓名一般三个字】 性别列4【男女占1,但是列标题两个汉字】
//限制1-255
public int exportFieldWidth();
//导出时是否进行字段转换 例如 性别用int存储,导出时可能转换为男,女
//若是sign为1,则需要在pojo中加入一个方法 get字段名Convert()
//例如,字段sex ,需要加入 public String getSexConvert() 返回值为string
//若是sign为0,则不必管
public int exportConvertSign();
//导入数据是否需要转化 及 对已有的excel,是否需要将字段转为对应的数据
//若是sign为1,则需要在pojo中加入 void set字段名Convert(String text)
public int importConvertSign();
}
3、model类如下图所示:
@Entity
@Table(name = "JEECG_TUSER", schema = "")
public class Tuser implements java.io.Serializable {
// Fields
private String cid;
@Excel(exportName="真实姓名", exportConvertSign = 0, exportFieldWidth = 10, importConvertSign = 0)
private String realname;
@Excel(exportName="所属部门", exportConvertSign = 0, exportFieldWidth = 10, importConvertSign = 0)
private String org;
@Excel(exportName="邮箱", exportConvertSign = 0, exportFieldWidth = 10, importConvertSign = 0)
private String mail;
@Excel(exportName="手机", exportConvertSign = 0, exportFieldWidth = 10, importConvertSign = 0)
private String mobile;
@Excel(exportName="用户类型", exportConvertSign = 0, exportFieldWidth = 10, importConvertSign = 0)
private String usertype;
@Excel(exportName="状态", exportConvertSign = 0, exportFieldWidth = 10, importConvertSign = 0)
private String status;
@Excel(exportName="创建时间", exportConvertSign = 0, exportFieldWidth = 10, importConvertSign = 0)
private Date ccreatedatetime;
@Excel(exportName="修改时间", exportConvertSign = 0, exportFieldWidth = 10, importConvertSign = 0)
private Date cmodifydatetime;
@Excel(exportName="用户名", exportConvertSign = 0, exportFieldWidth = 10, importConvertSign = 0)
private String cname;
@Excel(exportName="密码", exportConvertSign = 0, exportFieldWidth = 30, importConvertSign = 0)
private String cpwd;
}
4、jsp页面如下图所示:
5、action处理如下图所示:
注意:
这个name对应后台的 private File upload; 约定,
还有uploadContentType(上传文件的MIME类型)和 uploadFileName(上传文件的文件名,该文件名不包括文件的路径)。
因此,
/**
* 跳转到excel导入导出页面
* @return
*
*/
public String goExcelOpt(){
return "excel-opt";
}
//供浏览器读取的信息
private String fileName;
private InputStream inputStream;
private String filedataFileName;
public String getFiledataFileName() {
return filedataFileName;
}
public void setFiledataFileName(String filedataFileName) {
this.filedataFileName = filedataFileName;
}
public InputStream getInputStream() {
return inputStream;
}
public void setInputStream(InputStream inputStream) {
this.inputStream = inputStream;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
/**
* 导出Excel
* @return
*
*/
public String exportExcel(){
fileName = "导出信息.xls";
try {
fileName = new String(fileName.getBytes(), "ISO8859-1");
inputStream = demoService.exportExcel();
} catch (Exception e) {
e.printStackTrace();
logger.error(ExceptionUtil.getExceptionMessage(e));
}
return "export";
}
/**
* 导入Excel
* @return
*
*/
public void importExcel(){
Json j = new Json();
try {
demoService.importExcel(filedataFileName, filedata);
j.setSuccess(true);
j.setMsg("文件导入成功!");
} catch (Exception e) {
j.setSuccess(false);
j.setMsg("文件导入失败!");
logger.error(ExceptionUtil.getExceptionMessage(e));
}
writeJson(j);
}
6、service如下图所示:
/**
* 导出excel
*/
@Override
public InputStream exportExcel() throws Exception{
//获取数据
String hql = "from Tuser t where 1=1 ";
List
7、util的工具类如下图所示:
【导出工具类】
/**
*
* @param title Sheet名字
* @param pojoClass Excel对象Class
* @param dataSet Excel对象数据List
* @param out 输出流
*/
public static void exportExcel(String title, Class> pojoClass,
Collection> dataSet, OutputStream out) {
// 使用userModel模式实现的,当excel文档出现10万级别的大数据文件可能导致OOM内存溢出
exportExcelInUserModel(title, pojoClass, dataSet, out);
// 使用eventModel实现,可以一边读一边处理,效率较高,但是实现复杂,暂时未实现
}
private static void exportExcelInUserModel(String title, Class> pojoClass,
Collection> dataSet, OutputStream out) {
try {
// 首先检查数据看是否是正确的
if (dataSet == null || dataSet.size() == 0) {
throw new Exception("导出数据为空!");
}
if (title == null || out == null || pojoClass == null) {
throw new Exception("传入参数不能为空!");
}
// 声明一个工作薄
Workbook workbook = new HSSFWorkbook();
// 生成一个表格
Sheet sheet = workbook.createSheet(title);
// 标题
List exportFieldTitle = new ArrayList();
List exportFieldWidth = new ArrayList();
// 拿到所有列名,以及导出的字段的get方法
List methodObj = new ArrayList();
Map convertMethod = new HashMap();
// 得到所有字段
Field fileds[] = pojoClass.getDeclaredFields();
// 遍历整个filed
for (int i = 0; i < fileds.length; i++) {
Field field = fileds[i];
Excel excel = field.getAnnotation(Excel.class);
// 如果设置了annottion
if (excel != null) {
// 添加到标题
exportFieldTitle.add(excel.exportName());
// 添加标题的列宽
exportFieldWidth.add(excel.exportFieldWidth());
// 添加到需要导出的字段的方法
String fieldname = field.getName();
// System.out.println(i+"列宽"+excel.exportName()+" "+excel.exportFieldWidth());
StringBuffer getMethodName = new StringBuffer("get");
getMethodName.append(fieldname.substring(0, 1)
.toUpperCase());
getMethodName.append(fieldname.substring(1));
Method getMethod = pojoClass.getMethod(getMethodName
.toString(), new Class[] {});
methodObj.add(getMethod);
if (excel.exportConvertSign() == 1) {
StringBuffer getConvertMethodName = new StringBuffer(
"get");
getConvertMethodName.append(fieldname.substring(0, 1)
.toUpperCase());
getConvertMethodName.append(fieldname.substring(1));
getConvertMethodName.append("Convert");
// System.out.println("convert: "+getConvertMethodName.toString());
Method getConvertMethod = pojoClass
.getMethod(getConvertMethodName.toString(),
new Class[] {});
convertMethod.put(getMethodName.toString(),
getConvertMethod);
}
}
}
int index = 0;
// 产生表格标题行
Row row = sheet.createRow(index);
for (int i = 0, exportFieldTitleSize = exportFieldTitle.size(); i < exportFieldTitleSize; i++) {
Cell cell = row.createCell(i);
// cell.setCellStyle(style);
RichTextString text = new HSSFRichTextString(exportFieldTitle
.get(i));
cell.setCellValue(text);
}
// 设置每行的列宽
for (int i = 0; i < exportFieldWidth.size(); i++) {
// 256=65280/255
sheet.setColumnWidth(i, 256 * exportFieldWidth.get(i));
}
Iterator its = dataSet.iterator();
// 循环插入剩下的集合
while (its.hasNext()) {
// 从第二行开始写,第一行是标题
index++;
row = sheet.createRow(index);
Object t = its.next();
for (int k = 0, methodObjSize = methodObj.size(); k < methodObjSize; k++) {
Cell cell = row.createCell(k);
Method getMethod = methodObj.get(k);
Object value = null;
if (convertMethod.containsKey(getMethod.getName())) {
Method cm = convertMethod.get(getMethod.getName());
value = cm.invoke(t, new Object[] {});
} else {
value = getMethod.invoke(t, new Object[] {});
}
cell.setCellValue(value == null ? "" : value.toString());
}
}
workbook.write(out);
} catch (Exception e) {
e.printStackTrace();
}
}
【导入工具类】
注意:
//先设置Cell的类型,然后就可以把纯数字作为String类型读进来了:
cell.setCellType(Cell.CELL_TYPE_STRING);
/**
* 导入 excel
* @param file
* @param pojoClass
* @param pattern
* @return
*/
public static Collection importExcel(File file ,Class pojoClass) {
Collection dist = new ArrayList();
try {
// 得到目标目标类的所有的字段列表
Field filed[] = pojoClass.getDeclaredFields();
// 将所有标有Annotation的字段,也就是允许导入数据的字段,放入到一个map中
Map fieldSetMap = new HashMap();
Map fieldSetConvertMap = new HashMap();
// 循环读取所有字段
for (int i = 0; i < filed.length; i++) {
Field f = filed[i];
// 得到单个字段上的Annotation
Excel excel = f.getAnnotation(Excel.class);
// 如果标识了Annotationd的话
if (excel != null) {
// 构造设置了Annotation的字段的Setter方法
String fieldname = f.getName();
String setMethodName = "set"
+ fieldname.substring(0, 1).toUpperCase()
+ fieldname.substring(1);
// 构造调用的method,
Method setMethod = pojoClass.getMethod(setMethodName,
new Class[] { f.getType() });
// 将这个method以Annotaion的名字为key来存入。
//对于重名将导致 覆盖 失败,对于此处的限制需要
fieldSetMap.put(excel.exportName(), setMethod);
if(excel.importConvertSign()==1)
{
StringBuffer setConvertMethodName = new StringBuffer("set");
setConvertMethodName.append(fieldname.substring(0, 1)
.toUpperCase());
setConvertMethodName.append(fieldname.substring(1));
setConvertMethodName.append("Convert");
Method getConvertMethod = pojoClass.getMethod(setConvertMethodName.toString(),
new Class[] {String.class});
fieldSetConvertMap.put(excel.exportName(), getConvertMethod);
}
}
}
// 将传入的File构造为FileInputStream;
FileInputStream in = new FileInputStream(file);
// // 得到工作表
HSSFWorkbook book = new HSSFWorkbook(in);
// // 得到第一页
HSSFSheet sheet = book.getSheetAt(0);
// // 得到第一面的所有行
Iterator row = sheet.rowIterator();
// 得到第一行,也就是标题行
Row title = row.next();
// 得到第一行的所有列
Iterator cellTitle = title.cellIterator();
// 将标题的文字内容放入到一个map中。
Map titlemap = new HashMap();
// 从标题第一列开始
int i = 0;
// 循环标题所有的列
while (cellTitle.hasNext()) {
Cell cell = cellTitle.next();
String value = cell.getStringCellValue();
titlemap.put(i, value);
i = i + 1;
}
//用来格式化日期的DateFormat
SimpleDateFormat sf;
while (row.hasNext()) {
// 标题下的第一行
Row rown = row.next();
// 行的所有列
Iterator cellbody = rown.cellIterator();
// 得到传入类的实例
Object tObject = pojoClass.newInstance();
int k = 0;
// 遍历一行的列
while (cellbody.hasNext()) {
Cell cell = cellbody.next();
// 这里得到此列的对应的标题
String titleString = (String) titlemap.get(k);
// 如果这一列的标题和类中的某一列的Annotation相同,那么则调用此类的的set方法,进行设值
if (fieldSetMap.containsKey(titleString)) {
Method setMethod = (Method) fieldSetMap.get(titleString);
//得到setter方法的参数
Type[] ts = setMethod.getGenericParameterTypes();
//只要一个参数
String xclass = ts[0].toString();
//判断参数类型
if (fieldSetConvertMap.containsKey(titleString)) {
fieldSetConvertMap.get(titleString).invoke(tObject,
cell.getStringCellValue());
} else {
if (xclass.equals("class java.lang.String")) {
//先设置Cell的类型,然后就可以把纯数字作为String类型读进来了:
cell.setCellType(Cell.CELL_TYPE_STRING);
setMethod.invoke(tObject, cell
.getStringCellValue());
}
else if (xclass.equals("class java.util.Date")) {
try{
setMethod.invoke(tObject, cell
.getDateCellValue());
}catch(Exception e){
}
}
else if (xclass.equals("class java.lang.Boolean")) {
setMethod.invoke(tObject, cell
.getBooleanCellValue());
}
else if (xclass.equals("class java.lang.Integer")) {
setMethod.invoke(tObject, new Integer(cell
.getStringCellValue()));
}else if(xclass. equals("class java.lang.Long"))
{
setMethod.invoke(tObject,new Long( cell.getStringCellValue()));
}
}
}
// 下一列
k = k + 1;
}
dist.add(tObject);
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
return dist;
} | |