Excel导出样式设置及导出报表源码详解

时间:2024-03-27 11:05:45

一、Excel 导入步骤:

step1: 功能入口:(三个参数 -- 第一个参数:(String)sheetName   第二个参数:( Strign[] ) 标题  第三个参数:(List< Object[] >)数据)

InputStream in = exportExcel(sheetName, title, datas);


step2: 定义一个数据流InputStream inputStream 

字节数组缓冲区ByteArraryOutputStream byteArraryOutputStream,

声明一个工作簿HSSFWorkbook workbook,生成一个表格HSSFSheet sheet,生成样式HSSFCellStyle style,设置样式style.setXxx(),生成字体HSSFFont font,字体应用于样式style.setFont(font),生成画图管理器HSSFPatriarch  patriarch = sheet.createDrawingPatriarch();


step3: 产生表格标题行HSSFRow row,遍历集合,处理数据,产生数据行


step4: 处理的数据放入单元格cell.setCellValue() , 数据写入文件workbook.write(byteArraryOutputStream);, 

创建一个大小与此输出流的当前大小的一个新分配缓冲区inputStream = new ByteArraryOutputStream(byteArraryOutputStream.toByteArrary());,并返回。


step5:定义打印的流,并填入文件地址生成文件:FileOutputStream fileOutputStream = new FileOutputStream("E:\\BenTigerkin\\tiger.xls");

    定义字节数组 byte[] bytes = new byte[8192]; int bytesRead = 0;

     打印进xls中,while(bytesRead  = in.read(bytes) != -1){

fileOutputStream .write(bytes,0,bytesRead);

 }

二、样式讲解:

// 声明一个工作薄
HSSFWorkbook workbook = new HSSFWorkbook();
// 生成一个表格
HSSFSheet sheet = workbook.createSheet(title);
// 设置表格默认列宽度为15个字节
sheet.setDefaultColumnWidth(15);
// 生成一个样式
HSSFCellStyle style = workbook.createCellStyle();
// 设置这些样式
style.setFillForegroundColor(HSSFColor.SKY_BLUE.index);//单元格背景色
style.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);//图案类型
style.setBorderBottom(HSSFCellStyle.BORDER_THIN);//单元格下边框为细线
style.setBorderLeft(HSSFCellStyle.BORDER_THIN);//单元格左边框
style.setBorderRight(HSSFCellStyle.BORDER_THIN);//单元格右边框
style.setBorderTop(HSSFCellStyle.BORDER_THIN);//单元格上边框
style.setAlignment(HSSFCellStyle.ALIGN_CENTER);//单元格水平对齐方式
style.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);//单元格垂直对齐方式
// 生成一个字体HSSFFont font = workbook.createFont();font.setColor(HSSFColor.VIOLET.index);font.setFontHeightInPoints((short) 12);//设定字体大小font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);////字体增粗// 把字体应用到当前的样式style.setFont(font);
    // 声明一个画图的*管理器
    HSSFPatriarch patriarch = sheet.createDrawingPatriarch();//这里要加一个包commons-codec-1.11.jar
    // 定义注释的大小和位置,详见文档
    HSSFComment comment = patriarch.createComment(new HSSFClientAnchor(0,0, 0, 0, (short) 4, 2, (short) 6, 5));//直线
//        new HSSFClientAnchor(dx1, dy1, dx2, dy2, col1, row1, col2, row2);
//dx1 第1个单元格中x轴的偏移量        //dy1 第1个单元格中y轴的偏移量    

//dx2 第2个单元格中x轴的偏移量        //dy2 第2个单元格中y轴的偏移量
// col1 第1个单元格的列号        //row1 第1个单元格的行号
//col2 第2个单元格的列号        //row2 第2个单元格的行号
// 设置注释内容
comment.setString(new HSSFRichTextString("可以在POI中添加注释!"));
// 设置注释作者,当鼠标移动到单元格上是可以在状态栏中看到该内容.
comment.setAuthor("author:xiaoling");


// 有图片时,设置行高为60px;
row.setHeightInPoints(60);
// 设置图片所在列宽度为80px,注意这里单位的一个换算
sheet.setColumnWidth(i, (int) (35.7 * 80));
// sheet.autoSizeColumn(i);
byte[] bsValue = (byte[]) value;
//处理照片位置 HSSFClientAnchor anchor = new HSSFClientAnchor(0, 0, 100, 50, col , row , col +1, row +1);
// 【图片左上角为(col, row)第row+1行col+1列
// 右下角为( col +1, row +1)第 col +1+1行row +1+1列,宽为100,高为50
HSSFClientAnchor anchor = new HSSFClientAnchor(0, 0, 1023, 255, (short) 5, index, (short) 5, index);
anchor.setAnchorType(2);
patriarch.createPicture(anchor, workbook.addPicture(
        bsValue, HSSFWorkbook.PICTURE_TYPE_JPEG));//通过Image.createImage(width,height);可以得到一份图片的copy。
                                                    //也就是说:我们可以把某个图片缓存在内存中,然后随时可以把它调出来用。

Excel导出样式设置及导出报表源码详解

更详细的样式属性请参考:http://m.knowsky.com/1052185.html


三、具体代码(可以作为公用类)

public class CreateExcel {
public static InputStream exportExcel(String title, String[] headers,List<Object[]> dataset) {//tittle:sheetName;headers:标题;dataset:数据



 InputStream is;
    ByteArrayOutputStream os = new ByteArrayOutputStream();
    // 声明一个工作薄
    HSSFWorkbook workbook = new HSSFWorkbook();
    // 生成一个表格
    HSSFSheet sheet = workbook.createSheet(title);
    // 设置表格默认列宽度为15个字节
    sheet.setDefaultColumnWidth(15);
    // 生成一个样式
    HSSFCellStyle style = workbook.createCellStyle();
    // 设置这些样式
    style.setFillForegroundColor(HSSFColor.SKY_BLUE.index);//单元格背景色
    style.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);//图案类型
    style.setBorderBottom(HSSFCellStyle.BORDER_THIN);//单元格下边框为细线
    style.setBorderLeft(HSSFCellStyle.BORDER_THIN);//单元格左边框
    style.setBorderRight(HSSFCellStyle.BORDER_THIN);//单元格右边框
    style.setBorderTop(HSSFCellStyle.BORDER_THIN);//单元格上边框
    style.setAlignment(HSSFCellStyle.ALIGN_CENTER);//单元格水平对齐方式
    // 生成一个字体
    HSSFFont font = workbook.createFont();
    font.setColor(HSSFColor.VIOLET.index);
    font.setFontHeightInPoints((short) 12);//设定字体大小
    font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);////字体增粗
    // 把字体应用到当前的样式
    style.setFont(font);
    // 生成并设置另一个样式
    HSSFCellStyle style2 = workbook.createCellStyle();
    style2.setFillForegroundColor(HSSFColor.LIGHT_YELLOW.index);
    style2.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
    style2.setBorderBottom(HSSFCellStyle.BORDER_THIN);
    style2.setBorderLeft(HSSFCellStyle.BORDER_THIN);
    style2.setBorderRight(HSSFCellStyle.BORDER_THIN);
    style2.setBorderTop(HSSFCellStyle.BORDER_THIN);
    style2.setAlignment(HSSFCellStyle.ALIGN_CENTER);
    style2.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);//单元格垂直对齐方式
    // 生成另一个字体
    HSSFFont font2 = workbook.createFont();
    font2.setBoldweight(HSSFFont.BOLDWEIGHT_NORMAL);//字体增粗
    // 把字体应用到当前的样式
    style2.setFont(font2);
    // 声明一个画图的*管理器
    HSSFPatriarch patriarch = sheet.createDrawingPatriarch();//这里要加一个包commons-codec-1.11.jar
    // 定义注释的大小和位置,详见文档
    HSSFComment comment = patriarch.createComment(new HSSFClientAnchor(0, 0, 0, 0, (short) 4, 2, (short) 6, 5));//直线
    // 设置注释内容
    comment.setString(new HSSFRichTextString("可以在POI中添加注释!"));
    // 设置注释作者,当鼠标移动到单元格上是可以在状态栏中看到该内容.
    comment.setAuthor("author:xiaoling");
    // 产生表格标题行
    HSSFRow row = sheet.createRow(0);
    for (int i = 0; i < headers.length; i++) {
        HSSFCell cell = row.createCell(i);
        cell.setCellStyle(style);
        HSSFRichTextString text = new HSSFRichTextString(headers[i]);
        cell.setCellValue(text);
    }
    sheet.setColumnWidth(2, (int)35.7 * 800);
    sheet.setColumnWidth(3, (int)35.7 * 800);
    // 遍历集合数据,产生数据行
    int index = 0;
    for (Object[] o : dataset) {
        index++;
        row = sheet.createRow(index);
        for (int i = 0; i < o.length; i++) {
            HSSFCell cell = row.createCell(i);
            cell.setCellStyle(style2);
            try {
                Object value = o[i];
                if(value== null){
                    value="";
                }
                // 判断值的类型后进行强制类型转换
                String textValue = null;
                if (value instanceof Boolean) {
                    boolean bValue = (Boolean) value;
                    textValue = "男";
                    if (!bValue) {
                        textValue = "女";
                    }
                } else if (value instanceof Date) {
                    Date date = (Date) value;
                    SimpleDateFormat sdf = new SimpleDateFormat("Y-m-d H:i:s");
                    textValue = sdf.format(date);
                } else if (value instanceof byte[]) {
                    // 有图片时,设置行高为60px;
                    row.setHeightInPoints(60);
                    // 设置图片所在列宽度为80px,注意这里单位的一个换算
                    sheet.setColumnWidth(i, (int) (35.7 * 80));
                    // sheet.autoSizeColumn(i);
                    byte[] bsValue = (byte[]) value;
                    HSSFClientAnchor anchor = new HSSFClientAnchor(0, 0,
                            1023, 255, (short) i, index, (short) i, index);
                    anchor.setAnchorType(2);
                    patriarch.createPicture(anchor, workbook.addPicture(
                            bsValue, HSSFWorkbook.PICTURE_TYPE_JPEG));
                                         //也就是说:我们可以把某个图片缓存在内存中,然后随时可以把它调出来用。
                } else {
                    // 其它数据类型都当作字符串简单处理
                    textValue = value.toString();
                }
                // 如果不是图片数据,就利用正则表达式判断textValue是否全部由数字组成
                if (textValue != null) {
                    Pattern p = Pattern.compile("^//d+(//.//d+)?$");
                    Matcher matcher = p.matcher(textValue);
                    if (matcher.matches()) {
                        // 是数字当作double处理
                        cell.setCellValue(Double.parseDouble(textValue));
                    } else {
                        HSSFRichTextString richString = new HSSFRichTextString(
                                textValue);
                        HSSFFont font3 = workbook.createFont();
                        font3.setColor(HSSFColor.BLUE.index);
                        richString.applyFont(font3);
                        cell.setCellValue(richString);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                // 清理资源 
           }
        }
    }
    try {
        workbook.write(os);//数据写入文件
    } catch (IOException e) { 
       e.printStackTrace();
    }
    is = new ByteArrayInputStream(os.toByteArray());//创建一个大小与此输出流的当前大小的一个新分配缓冲区
    return is;
    }
} 注意:性别:true:男 false:女

图片要传读出的字节byte[] 


我做的只是一个main()测试类,直接把所有的代码写在一个类里面,

public static void main(String args[]) throws IOException {

    String[] headers = new String[]{"id", "name", "sno", "sex", "idCard", "pic"};
    String filePath = "E:\\\\Testling\\\\bx_ico.png";
    byte[] bytePic = redPic(filePath);//读取图片

    StudentInfoDao studentInfoDao = new StudentInfoDao();
    List<Object[]> studentInfoDaos = new ArrayList<>();
    Object[] object1 = new Object[]{1, "xiaoling", "11311010030", true, "342901199402222251", bytePic};
    Object[] object2 = new Object[]{2, "xaioxiang", "11311010031", false, "342901195302215242", bytePic};
    Object[] object3 = new Object[]{3, "xiaopiqiu", "11311010032", true, "342901199502212233", bytePic};
    studentInfoDaos.add(0, object1);
    studentInfoDaos.add(1, object2);
    studentInfoDaos.add(2, object3);
    /** 功能入口  */
    InputStream in = exportExcel("xiaolingTitle", headers, studentInfoDaos);

    FileOutputStream outputStream = null;

    try{
        //定义打印的流,并且填入文件地址生成文件
         outputStream = new FileOutputStream("E:\\xaioling123.xls");
        //定义字节数组
        byte[] bytes = new byte[8192];
        int bytesRead = 0;
        //打印进xls中
        while ((bytesRead = in.read(bytes)) != -1) {
            outputStream.write(bytes, 0, bytesRead);
        }
    }catch (Exception e) {
        e.printStackTrace();
    } finally {//先打开的后关闭,后打开的先关闭
        if (outputStream != null) {
            try {
                outputStream.flush();
                outputStream.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        if (in != null) {
            try {
                in.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

/** 读取图片数据 */
private static byte[] redPic(String filePath) {

    File file = new File(filePath);
    long fileSize = file.length();
    if (fileSize > Integer.MAX_VALUE) {
        System.out.println("file too big...");
        return null;
    }
    FileInputStream fi = null;
    try {
        fi = new FileInputStream(file);
    } catch (Exception e) {
        e.printStackTrace();
    }
    byte[] buffer = new byte[(int) fileSize];
    int offset = 0;
    int numRead = 0;
    try {
        while (offset < buffer.length
                && (numRead = fi.read(buffer, offset, buffer.length - offset)) >= 0) {
            offset += numRead;
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    // 确保所有数据均被读取
    if (offset != buffer.length) {
        try {
            throw new IOException("Could not completely read file "
                    + file.getName());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    try {
        fi.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return buffer;
}

四、整个过程中总结的小笔记

利用request.getHeader("user-agent")获取客户端浏览器和操作系统信息搜索 


String Agent = request.getHeader("User-Agent");
StringTokenizer st = new StringTokenizer(Agent,";");
st.nextToken();
//得到用户的浏览器名

String userbrowser = st.nextToken();
//得到用户的操作系统名

String useros = st.nextToken();

取得本机的信息也可以这样:操作系统信息
System.getProperty("os.version");
request.getHeader(“User-agent”)返回客户端浏览器的版本号、类型

getHeader(String name):获得http协议定义的传送文件头信息,
request. getMethod():获得客户端向服务器端传送数据的方法有GET、POST、PUT等类型
request. getRequestURI():获得发出请求字符串的客户端地址
request. getServletPath():获得客户端所请求的脚本文件的文件路径
request. getServerName():获得服务器的名字
request.getServerPort():获得服务器的端口号
request.getRemoteAddr():获得客户端的IP地址
request.getRemoteHost():获得客户端电脑的名字,若失败,则返回客户端电脑的IP地址
request.getProtocol():request.getHeaderNames():返回所有request header的名字,结果集是一个Enumeration(枚举)类的实例
request.getHeaders(String name):返回指定名字的request header的所有值,结果集是一个Enumeration(枚举)类的实例


InputStream哪里可以读取文件?它是一个Abstract的类,根本不可能实例化


InputStream是所有输入流的基类,FileInputStream是它的一个实现类,允许程序读取机器上面的文件 

类:   
HSSFWorkbook 创建 xls 的对象; HSSFWorkbook hw = new HSSFWorkbook();           
设置分区显示; hw.setRepeatingRowsAndColumns(sheet的index,行,列,行,列); 
HSSFSheet 创建 xls 中的sheet(工作表); HSSFSheet sheet = hw.createSheet("sheet1");//sheet1是 sheet 的名称可缺省   
设置列高; sheet.setColumnWidth((short)short,(short)short);   
HSSFRow 创建 xls 中的行; HSSFRow row = sheet.createRow(0); //0表示第一行   
设置行高; row.setHeight((short)short);   
HSSFFont 创建xls 中的字体; HSSFFont font = hw.createFont();   
设定字体大小; font.setFontHeightInPoints((short)54);   
设定为斜体; font.setItalic(true);   
设定文字删除线; font.setStrikeout(true);   
HSSFCellStyle 设定单元格风格; HSSFCellStyle style  = wb.createCellStyle();   
加入字体; style.setFont(font);   
HSSFCell 设定单元格; HSSFCell cell = row.createCell((short)0);   
单元格水平对齐方式; style.setAlignment(align);         //单元格水平0:普通1:左对齐2:居中3:右对齐4:填充5:正当 6:居中选择   
单元格垂直对齐方式; style.setVerticalAlignment(align);       //单元格垂直 0 :居上   1:居中   2:居下   3:正当   
    
单元格下边框为细线; style.setBorderBottom((short)short);   
同上一命令一同使用,设置颜色; style.setBottomBorderColor((short)short);   
单元格左边框; style.setBorderLeft((short)short);  |  style.setLeftBorderColor((short)short);   
单元格右边框; style.setBorderRight((short)short); | style.setRightBorderColor((short)short);   
单元格上边框; style.setBorderTop((short)short);   |style.setTopBorderColor((short)short);   
单元格字符编号(中文); cell.setEncoding(HSSFCell.ENCODING_UTF_16);       //中文   
单元格显示的值; cell.setCellValue("中医药"); //值的类型有:double,int,String,Date,boolean   
单元格背景色; style.setFillForegroundColor((short)short);   
图案类型; style.setFillPattern((short)short);   
单元格合并; sheet.addMergedRegion(new   Region(行,   (short)列,   行,   (short)列));   
    
单元格风格加入; cell.setCellStyle(style);  

HSSFPatriarch patriarch = ((HSSFSheet)wb.getSheet("sheetname14")).createDrawingPatriarch();

//直线
HSSFClientAnchor clientAnchor1 = new HSSFClientAnchor(0, 0, 0, 0,
        (short) 4, 2, (short) 6, 5);
HSSFSimpleShape shape1 = patriarch.createSimpleShape(clientAnchor1);
shape1.setShapeType(HSSFSimpleShape.OBJECT_TYPE_LINE);

//圆圈(椭圆)
HSSFClientAnchor clientAnchor2 = new HSSFClientAnchor(0, 0, 0, 0,
        (short) 8, 4, (short) 6, 5);
HSSFSimpleShape shape2 = patriarch.createSimpleShape(clientAnchor2);
shape2.setShapeType(HSSFSimpleShape.OBJECT_TYPE_OVAL);

//正方形(长方形)
HSSFClientAnchor clientAnchor3 = new HSSFClientAnchor(0, 0, 0, 0,
        (short) 12, 6, (short) 6, 5);
HSSFSimpleShape shape3 = patriarch.createSimpleShape(clientAnchor3);
shape3.setShapeType(HSSFSimpleShape.OBJECT_TYPE_RECTANGLE);

//Textbox
HSSFClientAnchor clientAnchor4 = new HSSFClientAnchor(0, 0, 0, 0,
        (short) 14, 8, (short) 6, 5);
HSSFTextbox textbox = patriarch.createTextbox(clientAnchor4);
textbox.setString(new HSSFRichTextString("This is a test"));

查看源码发现ByteArrayOutputStream的close();是空实现;它其实就是通过一个缓冲的byte[] 对标准输出流进行封装(标准的输出流是打开一个文件 无缓冲); 也就是说它本身没啥好关闭的 能访问时byte[]这个buffer 没有被回收 FileOutputStream有close()方法。执行之后要关闭流


JAVA常用的节点流:      
文 件 FileInputStream FileOutputStrean FileReader FileWriter 文件进行处理的节点流。 
字符串 StringReader StringWriter 对字符串进行处理的节点流。  
数 组 ByteArrayInputStream ByteArrayOutputStreamCharArrayReader CharArrayWriter 对数组进行处理的节点流(对应的不再是文件,而是内存中的一个数组)。 
管 道 PipedInputStream PipedOutputStream PipedReaderPipedWriter对管道进行处理的节点流。  
常用处理流(关闭处理流使用关闭里面的节点流)     
缓冲流:BufferedInputStrean BufferedOutputStream BufferedReader BufferedWriter      ---增加缓冲功能,避免频繁读写硬盘。      
转换流:InputStreamReader OutputStreamReader实现字节流和字符流之间的转换。   
数据流 DataInputStream DataOutputStream 等-提供将基础数据类型写入到文件中,或者读取出来.  


流的关闭顺序   
一般情况下是:先打开的后关闭,后打开的先关闭  
另一种情况:看依赖关系,如果流a依赖流b,应该先关闭流a,再关闭流b。
例如,处理流a依赖节点流b,应该先关闭处理流a,再关闭节点流b  可以只关闭处理流,不用关闭节点流。
处理流关闭的时候,会调用其处理的节点流的关闭方法。
 注意:   如果将节点流关闭以后再关闭处理流,会抛出IO异常。  
 如果关闭了处理流,在关闭与之相关的节点流,也可能出现IO异常。
 
不关闭流的后果:因为打开了流扥与文件已经在应用,此时无法删除你已经打开的文件。另外,占用内存空间,但现在的电脑配置,你在运行过程中这方面(占用内存)也很难体现出来的。严格地说,规范化的编程一般都要记住要关闭输入输出流的。 

flush()意思是把缓冲区的内容强制的写出。 
因为操作系统的某些机制,为了防止一直不停地磁盘读写,所以有了延迟写入的概念,(注意不要和frush()刷新混淆了)主要用在IO中,即清空缓冲区数据,一般在读写流(stream)的时候,数据是先被读到了内存中,再把数据写到文件中,当你数据读完的时候不代表你的数据已经写完了,因为还有一部分有可能会留在内存这个缓冲区中。这时候如果你调用了close()方法关闭了读写流,那么这部分数据就会丢失,所以应该在关闭读写流之前先flush()。。