导入导出Excel的Java工具类ExcelUtil

在编写ExcelUtil之前,在网上查了一些资料。java中用来处理Excel的第三方开源项目主要就是POI和JXL。poi功能强大,但是比较耗资源,对于大数据量的导入导出性能不是太好;jxl功能简单,但是性能比较好。

由于本项目的导入导出更多关注性能问题,而且jxl提供的功能基本也都够用了,于是选择了jxl作为支持。

实战

导出就是将List转化为Excel(listToExcel)

导入就是将Excel转化为List(excelToList)

导入导出中会出现各种各样的问题,比如:数据源为空、有重复行等,我自定义了一个ExcelException异常类,用来处理这些问题。

ExcelException类

    package common.tool.excel;  

    public class ExcelException extends Exception {  

        public ExcelException() {
            // TODO Auto-generated constructor stub
        }  

        public ExcelException(String message) {
            super(message);
            // TODO Auto-generated constructor stub
        }  

        public ExcelException(Throwable cause) {
            super(cause);
            // TODO Auto-generated constructor stub
        }  

        public ExcelException(String message, Throwable cause) {
            super(message, cause);
            // TODO Auto-generated constructor stub
        }  

    }  

下面就是该文的主角ExcelUtil登场了,作为一个工具类,其内的所有方法都是静态的,方便使用。

ExcelUitl类

    /**
     * @author     : WH
     * @group      : tgb8
     * @Date       : 2014-1-2 下午9:13:21
     * @Comments   : 导入导出Excel工具类
     * @Version    : 1.0.0
     */  

    public class ExcelUtil  {  

        /**
         * @MethodName  : listToExcel
         * @Description : 导出Excel(可以导出到本地文件系统,也可以导出到浏览器,可自定义工作表大小)
         * @param list      数据源
         * @param fieldMap      类的英文属性和Excel中的中文列名的对应关系
         * 如果需要的是引用对象的属性,则英文属性使用类似于EL表达式的格式
         * 如:list中存放的都是student,student中又有college属性,而我们需要学院名称,则可以这样写
         * fieldMap.put("college.collegeName","学院名称")
         * @param sheetName 工作表的名称
         * @param sheetSize 每个工作表中记录的最大个数
         * @param out       导出流
         * @throws ExcelException
         */
        public static <T>  void   listToExcel (
                List<T> list ,
                LinkedHashMap<String,String> fieldMap,
                String sheetName,
                int sheetSize,
                OutputStream out
                ) throws ExcelException{  

            if(list.size()==0 || list==null){
                throw new ExcelException("数据源中没有任何数据");
            }  

            if(sheetSize>65535 || sheetSize<1){
                sheetSize=65535;
            }  

            //创建工作簿并发送到OutputStream指定的地方
            WritableWorkbook wwb;
            try {
                wwb = Workbook.createWorkbook(out);  

                //因为2003的Excel一个工作表最多可以有65536条记录,除去列头剩下65535条
                //所以如果记录太多,需要放到多个工作表中,其实就是个分页的过程
                //1.计算一共有多少个工作表
                double sheetNum=Math.ceil(list.size()/new Integer(sheetSize).doubleValue());  

                //2.创建相应的工作表,并向其中填充数据
                for(int i=0; i<sheetNum; i++){
                    //如果只有一个工作表的情况
                    if(1==sheetNum){
                        WritableSheet sheet=wwb.createSheet(sheetName, i);
                        fillSheet(sheet, list, fieldMap, 0, list.size()-1);  

                    //有多个工作表的情况
                    }else{
                        WritableSheet sheet=wwb.createSheet(sheetName+(i+1), i);  

                        //获取开始索引和结束索引
                        int firstIndex=i*sheetSize;
                        int lastIndex=(i+1)*sheetSize-1>list.size()-1 ? list.size()-1 : (i+1)*sheetSize-1;
                        //填充工作表
                        fillSheet(sheet, list, fieldMap, firstIndex, lastIndex);
                    }
                }  

                wwb.write();
                wwb.close();  

            }catch (Exception e) {
                e.printStackTrace();
                //如果是ExcelException,则直接抛出
                if(e instanceof ExcelException){
                    throw (ExcelException)e;  

                //否则将其它异常包装成ExcelException再抛出
                }else{
                    throw new ExcelException("导出Excel失败");
                }
            }  

        }  

        /**
         * @MethodName  : listToExcel
         * @Description : 导出Excel(可以导出到本地文件系统,也可以导出到浏览器,工作表大小为2003支持的最大值)
         * @param list      数据源
         * @param fieldMap      类的英文属性和Excel中的中文列名的对应关系
         * @param out       导出流
         * @throws ExcelException
         */
        public static  <T>  void   listToExcel (
                List<T> list ,
                LinkedHashMap<String,String> fieldMap,
                String sheetName,
                OutputStream out
                ) throws ExcelException{  

            listToExcel(list, fieldMap, sheetName, 65535, out);  

        }  

        /**
         * @MethodName  : listToExcel
         * @Description : 导出Excel(导出到浏览器,可以自定义工作表的大小)
         * @param list      数据源
         * @param fieldMap      类的英文属性和Excel中的中文列名的对应关系
         * @param sheetSize    每个工作表中记录的最大个数
         * @param response  使用response可以导出到浏览器
         * @throws ExcelException
         */
        public static  <T>  void   listToExcel (
                List<T> list ,
                LinkedHashMap<String,String> fieldMap,
                String sheetName,
                int sheetSize,
                HttpServletResponse response
                ) throws ExcelException{  

            //设置默认文件名为当前时间:年月日时分秒
            String fileName=new SimpleDateFormat("yyyyMMddhhmmss").format(new Date()).toString();  

            //设置response头信息
            response.reset();
            response.setContentType("application/vnd.ms-excel");        //改成输出excel文件
            response.setHeader("Content-disposition","attachment; filename="+fileName+".xls" );  

            //创建工作簿并发送到浏览器
            try {  

                OutputStream out=response.getOutputStream();
                listToExcel(list, fieldMap, sheetName, sheetSize,out );  

            } catch (Exception e) {
                e.printStackTrace();  

                //如果是ExcelException,则直接抛出
                if(e instanceof ExcelException){
                    throw (ExcelException)e;  

                //否则将其它异常包装成ExcelException再抛出
                }else{
                    throw new ExcelException("导出Excel失败");
                }
            }
        }  

        /**
         * @MethodName  : listToExcel
         * @Description : 导出Excel(导出到浏览器,工作表的大小是2003支持的最大值)
         * @param list      数据源
         * @param fieldMap      类的英文属性和Excel中的中文列名的对应关系
         * @param response  使用response可以导出到浏览器
         * @throws ExcelException
         */
        public static <T>  void   listToExcel (
                List<T> list ,
                LinkedHashMap<String,String> fieldMap,
                String sheetName,
                HttpServletResponse response
                ) throws ExcelException{  

            listToExcel(list, fieldMap, sheetName, 65535, response);
        }  

        /**
         * @MethodName          : excelToList
         * @Description             : 将Excel转化为List
         * @param in                    :承载着Excel的输入流
         * @param sheetIndex        :要导入的工作表序号
         * @param entityClass       :List中对象的类型(Excel中的每一行都要转化为该类型的对象)
         * @param fieldMap          :Excel中的中文列头和类的英文属性的对应关系Map
         * @param uniqueFields  :指定业务主键组合(即复合主键),这些列的组合不能重复
         * @return                      :List
         * @throws ExcelException
         */
        public static <T>  List<T>  excelToList(
                InputStream in,
                String sheetName,
                Class<T> entityClass,
                LinkedHashMap<String, String> fieldMap,
                String[] uniqueFields
                ) throws ExcelException{  

            //定义要返回的list
            List<T> resultList=new ArrayList<T>();  

            try {  

                //根据Excel数据源创建WorkBook
                Workbook wb=Workbook.getWorkbook(in);
                //获取工作表
                Sheet sheet=wb.getSheet(sheetName);  

                //获取工作表的有效行数
                int realRows=0;
                for(int i=0;i<sheet.getRows();i++){  

                    int nullCols=0;
                    for(int j=0;j<sheet.getColumns();j++){
                        Cell currentCell=sheet.getCell(j,i);
                        if(currentCell==null || "".equals(currentCell.getContents().toString())){
                            nullCols++;
                        }
                    }  

                    if(nullCols==sheet.getColumns()){
                        break;
                    }else{
                        realRows++;
                    }
                }  

                //如果Excel中没有数据则提示错误
                if(realRows<=1){
                    throw new ExcelException("Excel文件中没有任何数据");
                }  

                Cell[] firstRow=sheet.getRow(0);  

                String[] excelFieldNames=new String[firstRow.length];  

                //获取Excel中的列名
                for(int i=0;i<firstRow.length;i++){
                    excelFieldNames[i]=firstRow[i].getContents().toString().trim();
                }  

                //判断需要的字段在Excel中是否都存在
                boolean isExist=true;
                List<String> excelFieldList=Arrays.asList(excelFieldNames);
                for(String cnName : fieldMap.keySet()){
                    if(!excelFieldList.contains(cnName)){
                        isExist=false;
                        break;
                    }
                }  

                //如果有列名不存在,则抛出异常,提示错误
                if(!isExist){
                    throw new ExcelException("Excel中缺少必要的字段,或字段名称有误");
                }  

                //将列名和列号放入Map中,这样通过列名就可以拿到列号
                LinkedHashMap<String, Integer> colMap=new LinkedHashMap<String, Integer>();
                for(int i=0;i<excelFieldNames.length;i++){
                    colMap.put(excelFieldNames[i], firstRow[i].getColumn());
                }   

                //判断是否有重复行
                //1.获取uniqueFields指定的列
                Cell[][] uniqueCells=new Cell[uniqueFields.length][];
                for(int i=0;i<uniqueFields.length;i++){
                    int col=colMap.get(uniqueFields[i]);
                    uniqueCells[i]=sheet.getColumn(col);
                }  

                //2.从指定列中寻找重复行
                for(int i=1;i<realRows;i++){
                    int nullCols=0;
                    for(int j=0;j<uniqueFields.length;j++){
                        String currentContent=uniqueCells[j][i].getContents();
                        Cell sameCell=sheet.findCell(currentContent,
                                uniqueCells[j][i].getColumn(),
                                uniqueCells[j][i].getRow()+1,
                                uniqueCells[j][i].getColumn(),
                                uniqueCells[j][realRows-1].getRow(),
                                true);
                        if(sameCell!=null){
                            nullCols++;
                        }
                    }  

                    if(nullCols==uniqueFields.length){
                        throw new ExcelException("Excel中有重复行,请检查");
                    }
                }  

                //将sheet转换为list
                for(int i=1;i<realRows;i++){
                    //新建要转换的对象
                    T entity=entityClass.newInstance();  

                    //给对象中的字段赋值
                    for(Entry<String, String> entry : fieldMap.entrySet()){
                        //获取中文字段名
                        String cnNormalName=entry.getKey();
                        //获取英文字段名
                        String enNormalName=entry.getValue();
                        //根据中文字段名获取列号
                        int col=colMap.get(cnNormalName);  

                        //获取当前单元格中的内容
                        String content=sheet.getCell(col, i).getContents().toString().trim();  

                        //给对象赋值
                        setFieldValueByName(enNormalName, content, entity);
                    }  

                    resultList.add(entity);
                }
            } catch(Exception e){
                e.printStackTrace();
                //如果是ExcelException,则直接抛出
                if(e instanceof ExcelException){
                    throw (ExcelException)e;  

                //否则将其它异常包装成ExcelException再抛出
                }else{
                    e.printStackTrace();
                    throw new ExcelException("导入Excel失败");
                }
            }
            return resultList;
        }  

        /*<-------------------------辅助的私有方法----------------------------------------------->*/
        /**
         * @MethodName  : getFieldValueByName
         * @Description : 根据字段名获取字段值
         * @param fieldName 字段名
         * @param o 对象
         * @return  字段值
         */
        private static  Object getFieldValueByName(String fieldName, Object o) throws Exception{  

            Object value=null;
            Field field=getFieldByName(fieldName, o.getClass());  

            if(field !=null){
                field.setAccessible(true);
                value=field.get(o);
            }else{
                throw new ExcelException(o.getClass().getSimpleName() + "类不存在字段名 "+fieldName);
            }  

            return value;
        }  

        /**
         * @MethodName  : getFieldByName
         * @Description : 根据字段名获取字段
         * @param fieldName 字段名
         * @param clazz 包含该字段的类
         * @return 字段
         */
        private static Field getFieldByName(String fieldName, Class<?>  clazz){
            //拿到本类的所有字段
            Field[] selfFields=clazz.getDeclaredFields();  

            //如果本类中存在该字段,则返回
            for(Field field : selfFields){
                if(field.getName().equals(fieldName)){
                    return field;
                }
            }  

            //否则,查看父类中是否存在此字段,如果有则返回
            Class<?> superClazz=clazz.getSuperclass();
            if(superClazz!=null  &&  superClazz !=Object.class){
                return getFieldByName(fieldName, superClazz);
            }  

            //如果本类和父类都没有,则返回空
            return null;
        }  

        /**
         * @MethodName  : getFieldValueByNameSequence
         * @Description :
         * 根据带路径或不带路径的属性名获取属性值
         * 即接受简单属性名,如userName等,又接受带路径的属性名,如student.department.name等
         *
         * @param fieldNameSequence  带路径的属性名或简单属性名
         * @param o 对象
         * @return  属性值
         * @throws Exception
         */
        private static  Object getFieldValueByNameSequence(String fieldNameSequence, Object o) throws Exception{  

            Object value=null;  

            //将fieldNameSequence进行拆分
            String[] attributes=fieldNameSequence.split("\\.");
            if(attributes.length==1){
                value=getFieldValueByName(fieldNameSequence, o);
            }else{
                //根据属性名获取属性对象
                Object fieldObj=getFieldValueByName(attributes[0], o);
                String subFieldNameSequence=fieldNameSequence.substring(fieldNameSequence.indexOf(".")+1);
                value=getFieldValueByNameSequence(subFieldNameSequence, fieldObj);
            }
            return value;   

        }   

        /**
         * @MethodName  : setFieldValueByName
         * @Description : 根据字段名给对象的字段赋值
         * @param fieldName  字段名
         * @param fieldValue    字段值
         * @param o 对象
         */
        private static void setFieldValueByName(String fieldName,Object fieldValue,Object o) throws Exception{  

                Field field=getFieldByName(fieldName, o.getClass());
                if(field!=null){
                    field.setAccessible(true);
                    //获取字段类型
                    Class<?> fieldType = field.getType();    

                    //根据字段类型给字段赋值
                    if (String.class == fieldType) {
                        field.set(o, String.valueOf(fieldValue));
                    } else if ((Integer.TYPE == fieldType)
                            || (Integer.class == fieldType)) {
                        field.set(o, Integer.parseInt(fieldValue.toString()));
                    } else if ((Long.TYPE == fieldType)
                            || (Long.class == fieldType)) {
                        field.set(o, Long.valueOf(fieldValue.toString()));
                    } else if ((Float.TYPE == fieldType)
                            || (Float.class == fieldType)) {
                        field.set(o, Float.valueOf(fieldValue.toString()));
                    } else if ((Short.TYPE == fieldType)
                            || (Short.class == fieldType)) {
                        field.set(o, Short.valueOf(fieldValue.toString()));
                    } else if ((Double.TYPE == fieldType)
                            || (Double.class == fieldType)) {
                        field.set(o, Double.valueOf(fieldValue.toString()));
                    } else if (Character.TYPE == fieldType) {
                        if ((fieldValue!= null) && (fieldValue.toString().length() > 0)) {
                            field.set(o, Character
                                    .valueOf(fieldValue.toString().charAt(0)));
                        }
                    }else if(Date.class==fieldType){
                        field.set(o, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(fieldValue.toString()));
                    }else{
                        field.set(o, fieldValue);
                    }
                }else{
                    throw new ExcelException(o.getClass().getSimpleName() + "类不存在字段名 "+fieldName);
                }
        }  

        /**
         * @MethodName  : setColumnAutoSize
         * @Description : 设置工作表自动列宽和首行加粗
         * @param ws
         */
        private static void setColumnAutoSize(WritableSheet ws,int extraWith){
            //获取本列的最宽单元格的宽度
            for(int i=0;i<ws.getColumns();i++){
                int colWith=0;
                for(int j=0;j<ws.getRows();j++){
                    String content=ws.getCell(i,j).getContents().toString();
                    int cellWith=content.length();
                    if(colWith<cellWith){
                        colWith=cellWith;
                    }
                }
                //设置单元格的宽度为最宽宽度+额外宽度
                ws.setColumnView(i, colWith+extraWith);
            }  

        }  

        /**
         * @MethodName  : fillSheet
         * @Description : 向工作表中填充数据
         * @param sheet     工作表
         * @param list  数据源
         * @param fieldMap 中英文字段对应关系的Map
         * @param firstIndex    开始索引
         * @param lastIndex 结束索引
         */
        private static <T> void fillSheet(
                WritableSheet sheet,
                List<T> list,
                LinkedHashMap<String,String> fieldMap,
                int firstIndex,
                int lastIndex
                )throws Exception{  

            //定义存放英文字段名和中文字段名的数组
            String[] enFields=new String[fieldMap.size()];
            String[] cnFields=new String[fieldMap.size()];  

            //填充数组
            int count=0;
            for(Entry<String,String> entry:fieldMap.entrySet()){
                enFields[count]=entry.getKey();
                cnFields[count]=entry.getValue();
                count++;
            }
            //填充表头
            for(int i=0;i<cnFields.length;i++){
                Label label=new Label(i,0,cnFields[i]);
                sheet.addCell(label);
            }  

            //填充内容
            int rowNo=1;
            for(int index=firstIndex;index<=lastIndex;index++){
                //获取单个对象
                T item=list.get(index);
                for(int i=0;i<enFields.length;i++){
                    Object objValue=getFieldValueByNameSequence(enFields[i], item);
                    String fieldValue=objValue==null ? "" : objValue.toString();
                    Label label =new Label(i,rowNo,fieldValue);
                    sheet.addCell(label);
                }  

                rowNo++;
            }  

            //设置自动列宽
            setColumnAutoSize(sheet, 5);
        }  

    }  

该工具类有4个重载的导出方法和1个导入方法,大家可以根据实际情况进行选择。

总结

导入和导出方法都是通过传一个fieldMap参数(类的英文属性和Excel的中文列头的对应关系)来连接实体类和Excel的

导出的时候可以选择导出到本地文件系统或导出到浏览器,也可以自定义每个工作表的大小

导入的时候可以自定义业务主键组合uniqueFields,这样就可以检测Excel中是否有重复行了

时间: 2024-10-22 04:31:37

导入导出Excel的Java工具类ExcelUtil的相关文章

Java利用POI导入导出Excel中的数据

     首先谈一下今天发生的一件开心的事,本着一颗android的心我被分配到了PB组,身在曹营心在汉啊!好吧,今天要记录和分享的是Java利用POI导入导出Excel中的数据.下面POI包的下载地址http://poi.apache.org/download.html,有兴趣的朋友也可以去看看其中的API.      下面分享一下在对POI进行基本操作时觉得需要注意的两点:       1.POI中针对xlsx/xls是需要create different Workbook instance

java工具类-excel jxl

jxl-2.6.9.14.jarimport net.sf.jxls.transformer.XLSTransformer;//jxls-core-1.0.2.jarimport java.io.File; public File inputFile;     Workbook workbook = WorkbookFactory.create(inputFile);    Sheet sheet0 = workbook.getSheetAt(0);        int rowNum = sh

EasyPOI导入导出Excel

EasyPOI工具可以方便的写出Excel导出,Excel模板导出,Excel导入,Word模板导出,通过简单的注解和模板 导入maven依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <grou

jxl导入/导出excel

转自:http://www.cnblogs.com/linjiqin/p/3540266.html 1.jxl导入/导出excel案例,黏贴即可运行 package junit.test; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import jxl.Cell; import jxl.Sheet; import jxl.Workbook;

基于SSM的POI导入导出Excel实战第一篇-SSM框架的整合

业务背景:在JavaWeb应用开发中,经常需要将应用系统中某些业务数据导出到Excel中,又或者需要将这些业务数据先收集到Excel然后一键导入到系统 业务需求:如何用Java实现导入导出Excel 需求分析:目前流行的Java导入导出Excel的框架有POI跟JXL,这两者的优缺点在这里我就不作比较了,感兴趣的童鞋可以自行搜索了解一下; 技术选型:从本文开始,我将分享一下如何基于SSM框架+POI实现Java应用导入导出Excel,数据库采用mysql5.6,应用服务器采用tomcat7 工具

C#用Infragistics 导入导出Excel(一)

最近项目中有数据的导入导出Excel的需求,这里做简单整理. 公司用的是Infragistics的产品,付费,不需要本地安装Office. 有需要的朋友可以下载 Infragistics.2013.2.2098,提取密码:5u17 本文完整代码下载 Demo.Excel.zip 当然,我知道还有其他开源的类库来操作Excel,希望有资源的博友可以一起共享一下. Infragistics安装使用 直接安装Infragistics_WinForms_20132.msi后再项目Reference中引用

thinkphp导入导出excel表单数据

在PHP项目经常要导入导出Excel表单. 先去下载PHPExcel类库文件,放到相应位置. 我在thinkphp框架中的位置为ThinkPHP/Library/Org/Util/ 导入 在页面上传excel文件提交到后台,利用form表单提单即可 <form method="post" enctype="multipart/form-data" action="" > <input type="submit"

.Net MVC 导入导出Excel总结(三种导出Excel方法,一种导入Excel方法)

.Net MVC  导入导出Excel总结(三种导出Excel方法,一种导入Excel方法) 通过MVC控制器导出导入Excel文件(可用于java SSH架构) public class ExcelController : Controller { // // GET: /Excel/ Models.zbwxglEntities myMdl = new Models.zbwxglEntities(); /// <summary> /// 第一种方法,利用文件输出流进行读写操作 /// <

NPOI导入导出Excel

.net mvc利用NPOI导入导出excel 1.导出Excel :首先引用NPOI包,从这里下载>download(Action一定要用FileResult) /// <summary> /// 批量导出需要导出的列表 /// </summary> /// <returns></returns> public FileResult ExportStu2() { //获取list数据 var checkList = (from oc in db.Or