来啦,已经跑了好几圈啦....
Canvas is not supported in your browser.

poi

-----

这是导出时,容易出现的问题,后面说到

 

 设置字符编码:

Font font =wb.createFont();
font.setCharSet(font.ANSI_CHARSET);//注意这地方不能手写数字,这是常量



 

开始正文

一、黑历史

为什么要做excel打印:就是为了客户需求,有的客户习惯excel。

java操作excel主要分两类:

1、(全方位操作用)poi大概就是:属于apache的产品,操作microsoft excel word,ppt,visio等微软旗下所有的工具,支持office所有版本。

但是在poi早期,当时微软产品都时OLE2结构(底层就是2进制)文件,这是office2003以前;然而poi操作大数据时,就会有bug,然而jxl也是这种数据结构,但可以解决这问题,所以jxl当时比office厉害。

从office2007开始,微软就重新开发了office,底层使用OOXML结构,这种数据结构,可以操作大数据,所以excel底层就是xml格式文件。

2、(一般导入导出数据时用)jxl时仅用来操作excel,并且仅支持2003以下版本,不支持2007,也仅仅时OLE2文档结构。

 二、jar准备

        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>3.9</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>3.9</version>
        </dependency>

三、怎么使用(本质就是将内存中的数据通过流的方式写到硬盘上)

  1、训练

 第一节认清:excel怎么创建表然后怎么写内容然后再保存文件

  • 创建一个工作簿
  • 创建一个工作表,默认是3个工作表
  • 定位哪一个行
  • 定位哪一列
  • 单元格写内容(这前面5步都是在内存中进行的,不要被表面迷惑)
  • 点击保存(这将内存中数据序列化到硬盘上)
  • 关闭

 第二节:设置单元格内容

 

Workbook wb =new HSSFWorkbook();
    Sheet sh = wb.createSheet();
    Row row = sh.createRow(1);
    Cell cell = row.createCell(1);
    cell.setCellValue("我是中国人");
    FileOutputStream op =new FileOutputStream("D:\\B.xls");
    wb.write(op);执行完这一步,内容就写到硬盘上,即B.xls创建了
    op.close(); 如果还没执行这一步,那么excel进程提示被占用,不能编辑。关闭流后,才可以操作excel

第三节:设置样式(这和设置内容是不同的东西)--------重点

 

CellStyle cs=wb.createCellStyle();//注意这方法是工作簿的,单元格样式,excel无论是合并还是什么的都是单元格,还有边框,背景色等等都是样式,所以样式就是全局的
    Font font=wb.createFont();//创建字体,也只能工作簿拥有,是全局的,字体无论在哪一张工作表都是拥有工作簿提供的所有字体,所以这个方法只能是工作簿的
    font.setFontName("微软雅黑");//设置字体
    font.setFontHeightInPoints((short)24);//设置字体高度值点数,就是设置大小,点数相当于单位-------注意的地方,也可以用setFontHeight,但单位不同,需要换算,所以统一用setFontHeightInPoints,
    cs.setFont(font);设置字体样式
    cell.setCellStyle(cs);//设置单元格样式
创建样式对象-->创建字体对象-->设置字体对象各种样式-->设置字体-->设置单元格样式

 第四节:优化代码

为了避免重复new对象,造成内存损失,可以引用利用(引用被利用,没有被引用指向的对象会被垃圾回收期回收)java是一个引用同时间只能指向一个对象(不然虚拟机不知道调用那个对象),多个对象可以指向一个引用

A  n =  new Hero(); 

n =  new Hero();//同一个引用garen指向新创建的对象,上面那个对象就会被垃圾回收机制回收(多个对象可以指向一个引用)

 

1设置单元格内容再利用:

     row1 = sh.createRow(2); 再利用 row引用
     cell2 = row1.createCell(2);在利用cell引用
    cell2.setCellValue("woshizhongguoren ");

2设置样式再利用(因为是全局的,所以会出现后者覆盖前者,所以不能直接利用font,cs,)

所以必须font 和cs初始化

 

font= wb.createFont();再利用font引用
cs = wb.createCellStyle();再利用cs引用
font.setFontHeightInPoints((short)18);
font.setFontName("隶书");
font.setBoldweight(Font.BOLDWEIGHT_BOLD);
cs.setFont(font);
cell.setCellStyle(cs);

 

 


 

 以上代码可以抽取出来:

excel 标题样式一般相同,内容样式一般相同,简化代码,每次调用方法前,初始化对象

Workbook wb =new HSSFWorkbook();
    Sheet sh = wb.createSheet();
    
    Row row = sh.createRow(1);
    Cell cell = row.createCell(1);
    cell.setCellValue("我是中国人");
    CellStyle cs=wb.createCellStyle();        
    Font font=wb.createFont();
    this.getCellStylefont(font, cs, cell);
    
    
   初始化: 
    row = sh.createRow(2);
    cell = row.createCell(2);
    cell.setCellValue("woshizhongguoren ");
     font= wb.createFont();
     cs = wb.createCellStyle();
     this.getCellStyletext(font, cs, cell);

 

这是excel标题
public
CellStyle getCellStyleTitle(Font font,CellStyle cs,Cell cell){ font.setFontHeightInPoints((short)18); font.setFontName("隶书"); font.setBoldweight(Font.BOLDWEIGHT_BOLD); cs.setFont(font); cell.setCellStyle(cs); return cs; }
这是excel 内容
public
CellStyle getCellStyletext(Font font,CellStyle cs,Cell cell){ font.setFontName("微软雅黑"); font.setFontHeightInPoints((short)85); cs.setFont(font); cell.setCellStyle(cs); return cs; }

 

  2、项目运用

 打印一张出货表:

日期处理可以数据库处理,也可以在poi处理

 

加个链接,添加打印按钮,根据日期,后台通过sql查询,返回list集合,通过循环遍历导出一张excel表

添加单元格数据:

List<OutProduct> outProductList = outProductService.find(paraMap);
        HSSFWorkbook wb =new HSSFWorkbook();
        HSSFSheet sh = wb.createSheet();
        int i=1;  //定义列起始索引
        int j=0;//定义行起始索引
        HSSFRow cr ;//声明行局部变量
        HSSFCell cc;//声明单元格局部变量
        String[] arr=new String[]{"客户","订单号","货号","数量","工厂","工厂交期","船期","贸易条款"};  //标题栏
        cr=sh.createRow(j++);
        for (String val : arr) {
            cc=cr.createCell(i++);
            cc.setCellValue(val);
        }
        for(OutProduct op: outProductList){                                                    //数据栏
            i=1;  //初始化列起始索引
            cr=sh.createRow(j++); j++就是j先赋值,然后再自增
            cc=cr.createCell(i++);
            String customName = op.getCustomName();
            cc.setCellValue(customName);
            
            cc=cr.createCell(i++);
            String contractNo = op.getContractNo();
            cc.setCellValue(contractNo);
            
            cc=cr.createCell(i++);
            String productNo = op.getProductNo();
            cc.setCellValue(productNo);
            
            cc=cr.createCell(i++);
            String boxNum = op.getBoxNum();
            cc.setCellValue(boxNum);
            
            cc=cr.createCell(i++);
            String factoryName = op.getFactoryName();
            cc.setCellValue(factoryName);
            
            cc=cr.createCell(i++);
            String deliveryPeriod = op.getDeliveryPeriod();
            cc.setCellValue(deliveryPeriod);
            
            cc=cr.createCell(i++);
            String shipTime = op.getShipTime();
            cc.setCellValue(shipTime);
            
            cc=cr.createCell(i++);
            String tradeTerms = op.getTradeTerms();
            cc.setCellValue(tradeTerms);
            
            
        }
        OutputStream out =new FileOutputStream("d://a.xls");
        wb.write(out);
        out.close();

这里可以看出j++好处,中间插入一个大标题,下面的内容自动往下移动

这部分仍是添加单元格内容:大标题是合并单元格,

所以表格的合并单元格方法,指定4个参数,起始行,结束行,起始列,结束列

然后怎么做到“2018年8月份出货表”,时间是动态的,前台传过来的,

replaceFirst成功则返回替换的字符串,失败则返回原始字符串,所以要先进行-0替换,大范围替换,没有0则返回原始字符串,然后小范围-替换成年

    sh.addMergedRegion(new CellRangeAddress(0, 0, 1, 8)); //合并单元格默认是设置在区域的第一行第一列
        cr=sh.createRow(j++);
        cc=cr.createCell(1);
        cc.setCellValue(pdate.replaceFirst("-0", "年").replaceFirst("-", "年")+"月份出货表");

 

 

 添加单元格样式:

 设置字体和样式:

这两个对象都是workbook对象创建的,字体包含在样式中

HSSFCellStyle nstyle = wb.createCellStyle();
        HSSFFont nfont = wb.createFont();
        bigTitle(wb, nstyle, nfont);
        cc.setCellStyle(nstyle);

行高是行对象创建,

cr.setHeightInPoints(36);

 字体加粗是字体对象

单元格内容居中这是样式对象,样式CellStyle中有横向和竖向常量

public void bigTitle(Workbook wb,CellStyle nstyle,Font nfont){
        nfont.setFontHeightInPoints((short)16);
        nfont.setFontName("宋体");
        nstyle.setFont(nfont);
        nfont.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);//加粗,这是常量,是HSSFFont类中的常量

nstyle.setAlignment(CellStyle.ALIGN_CENTER);横向居中
nstyle.setVerticalAlignment(CellStyle.VERTICAL_CENTER);纵向居中


    }
	设置边框
		nstyle.setBorderBottom(CellStyle.BORDER_THIN);
		nstyle.setBorderLeft(CellStyle.BORDER_THIN);
		nstyle.setBorderRight(CellStyle.BORDER_THIN);
		nstyle.setBorderTop(CellStyle.BORDER_THIN);

  分大标题 标题 正文设置样式,每次设置样式钱需要初始化,

     初始化  nstyle = wb.createCellStyle();
		nfont = wb.createFont();
		

  

public void bigTitle(Workbook wb,CellStyle nstyle,Font nfont){
        nfont.setFontHeightInPoints((short)16);
        nfont.setFontName("宋体");
        nfont.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
        nstyle.setAlignment(CellStyle.ALIGN_CENTER);
        nstyle.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
        nstyle.setFont(nfont);
        
    }
    public CellStyle title(Workbook wb,CellStyle nstyle,Font nfont){
        nfont.setFontHeightInPoints((short)12);
        nfont.setFontName("黑体");
        
        nstyle.setAlignment(CellStyle.ALIGN_CENTER);
        nstyle.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
        
        nstyle.setBorderBottom(CellStyle.BORDER_THIN);
        nstyle.setBorderLeft(CellStyle.BORDER_THIN);
        nstyle.setBorderRight(CellStyle.BORDER_THIN);
        nstyle.setBorderTop(CellStyle.BORDER_THIN);
        nstyle.setFont(nfont);
        return nstyle;
        
    }
    public CellStyle text(Workbook wb,CellStyle nstyle,Font nfont){
        nfont.setFontHeightInPoints((short)10);
        nfont.setFontName("Times New Roman");
        nstyle.setAlignment(CellStyle.ALIGN_LEFT);
        nstyle.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
        nstyle.setFont(nfont);
        return nstyle;
    }

 

 

 列宽特殊,这是表对象的,sheet:通过源码可以得出,需要乘以256,这是api bug

然而还是差那么一点点,这是poi一个bug,不能够精确。所以经过测试一般接近准确*300,

 a列是为了装订线的位置

 

 

打印时,不能在一页显示,可以设置横向,拖到一页,可以设置页面方向

设置页眉页脚,设置重复标题行

 


 上面所说的都是下载,用户体验不好,项目中都是模板开发,用户需求变更,只要改变模板上的样式,并且模板的样式都是通过excel手动设置,不需要在代码中设置,代码中只要获取一行模板样式,后面的内容全设置成模板样式就可以了,

可以解决上面所有问题。不然每次输出的文件在服务器端,用户没法看,

 项目上传下载 用工具类


 

 

        ByteArrayOutputStream bo=new ByteArrayOutputStream();
        wb.write(bo);
        
        DownloadUtil du=new DownloadUtil();
        du.download(bo, response, "出货表.xls");
工具类: * @param byteArrayOutputStream 将文件内容写入ByteArrayOutputStream
     * @param response HttpServletResponse    写入response
     * @param returnName 返回的文件名
     */
    public void download(ByteArrayOutputStream byteArrayOutputStream, HttpServletResponse response, String returnName) throws IOException{
        response.setContentType("application/octet-stream;charset=utf-8");
        returnName = response.encodeURL(new String(returnName.getBytes(),"iso8859-1"));            //保存的文件名,必须和页面编码一致,否则乱码
        response.addHeader("Content-Disposition",   "attachment;filename=" + returnName);  
        response.setContentLength(byteArrayOutputStream.size());
        
        ServletOutputStream outputstream = response.getOutputStream();    //取得输出流
        byteArrayOutputStream.writeTo(outputstream);                    //写到输出流
        byteArrayOutputStream.close();                                    //关闭
        outputstream.flush();                                            //刷数据
    }

 通过模板开发:下面是模板

主要步骤:读取服务器上的模板文件到内存中,然后在内存中对内存中的模板进行大标题:设置动态值,标题:不用管了,用内存中模板标题,内容上:设置内容、样式和模板一行内容样式一样

 

 

    @RequestMapping("/cargo/outproduct/outProductPrint.action")
    public void print(String inputDate, HttpServletResponse response) throws FileNotFoundException, IOException, ParseException{
        /*
         * 操作步骤:
         * 1、获取数据
         * 2、POI写数据到文件
         */
        List<OutProduct> oList = outProductService.findOutProduct(inputDate+"%");
        
        Workbook wb = new HSSFWorkbook(new FileInputStream(new File("c:\\tFACTORY.xls")));                //打开模板文件
        Sheet sheet = wb.getSheetAt(0);                        //打开第一个工作表
        Row nRow = null;
        Cell nCell = null;
        int rowNo = 2;                                        //行号
        int colNo = 1;                                        //列号
        
        //处理标题
        nRow = sheet.getRow(0);                                //获得行对象
        nCell = nRow.getCell(1);                            //获得单元格对象
        nCell.setCellValue(inputDate.replaceFirst("-0", "-").replaceFirst("-", "年")+"月份出货表");            //yyyy-MM 2010-08
        
        //获取模板文件中的样式
        nRow = sheet.getRow(2);
        nCell = nRow.getCell(1);
        CellStyle customNameStyle = nCell.getCellStyle();            //获取客户名称样式
        
        nRow = sheet.getRow(2);
        nCell = nRow.getCell(2);
        CellStyle contractNoStyle = nCell.getCellStyle();
        
        nRow = sheet.getRow(2);
        nCell = nRow.getCell(3);
        CellStyle productNoStyle = nCell.getCellStyle();
        
        nRow = sheet.getRow(2);
        nCell = nRow.getCell(4);
        CellStyle cnumberStyle = nCell.getCellStyle();
        
        nRow = sheet.getRow(2);
        nCell = nRow.getCell(5);
        CellStyle factoryStyle = nCell.getCellStyle();
        
        nRow = sheet.getRow(2);
        nCell = nRow.getCell(6);
        CellStyle extStyle = nCell.getCellStyle();
        
        nRow = sheet.getRow(2);
        nCell = nRow.getCell(7);
        CellStyle dateStyle = nCell.getCellStyle();
        
        nRow = sheet.getRow(2);
        nCell = nRow.getCell(9);
        CellStyle tradeTermsStyle = nCell.getCellStyle();
        
        for(int i=0;i<oList.size();i++){
            colNo = 1;
            OutProduct op = oList.get(i);                     //获取每个出货表对象
            
            nRow = sheet.createRow(rowNo++);                //创建行
            nRow.setHeightInPoints(24);                        //行高
            
            nCell = nRow.createCell(colNo++);                //创建单元格
            nCell.setCellValue(op.getCustomName());
            nCell.setCellStyle(customNameStyle);
            
            nCell = nRow.createCell(colNo++);
            nCell.setCellValue(op.getContractNo());
            nCell.setCellStyle(contractNoStyle);
            
            nCell = nRow.createCell(colNo++);
            nCell.setCellValue(op.getProductNo());
            nCell.setCellStyle(productNoStyle);
            
            nCell = nRow.createCell(colNo++);
            nCell.setCellValue(op.getCnumber());
            nCell.setCellStyle(cnumberStyle);
            
            nCell = nRow.createCell(colNo++);
            nCell.setCellValue(op.getFactoryName());
            nCell.setCellStyle(factoryStyle);
            
            nCell = nRow.createCell(colNo++);
            nCell.setCellValue("附件");

            List<String> extNameList = outProductService.getExtName(op.getContractProductId());
            String _extName = "";
            if(extNameList!=null&&extNameList.size()>0){
                for(String extName : extNameList){
                    _extName += extName + "\n";                //换行符
                }
                _extName = _extName.substring(0,_extName.length()-1);        //去掉最后一个字符
            }else{
                _extName = "无";
            }
            nCell.setCellValue(_extName);
            nCell.setCellStyle(extStyle);
            
            nCell = nRow.createCell(colNo++);
            //nCell.setCellValue(UtilFuns.dateTimeFormat(op.getDeliveryPeriod()));        //利用工具类转类型,同时进行格式化
            nCell.setCellValue(op.getDeliveryPeriod());
            nCell.setCellStyle(dateStyle);
            
            nCell = nRow.createCell(colNo++);
            //nCell.setCellValue(UtilFuns.dateTimeFormat(op.getShipTime()));
            nCell.setCellValue(op.getShipTime());
            nCell.setCellStyle(dateStyle);
            
            nCell = nRow.createCell(colNo++);
            nCell.setCellValue(op.getTradeTerms());
            nCell.setCellStyle(tradeTermsStyle);
        }
        
        
        DownloadUtil du = new DownloadUtil();

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();            //生成流对象
        wb.write(byteArrayOutputStream);
        du.download(byteArrayOutputStream, response, "出货表.xls");        //弹出下载框,用户就可以直接下载
    }
    

 下面是导出下载后的结果:

 

 

 

 

String path=request.getSession().getServletContext().getRealPath("/")+"/make/xlsprint";//必须是/的虚拟路径,不然jdk1.8不会拼接/后面的字符串  ,这红色的斜杠最好加上,多个斜杠,底层会处理,不要紧
        InputStream is =new FileInputStream(new File(path+"/tOUTPRODUCT.xls"));//这里红色/也一样,不加也可以
        HSSFWorkbook wb =new HSSFWorkbook(is);

 

posted @ 2018-10-18 10:06  fangpengcheng_方鹏程  阅读(323)  评论(0编辑  收藏  举报