POI导出excel性能问题

POI导出excel性能问题

认识Excel

Excel有2种大的版本,20032007(2010/2013总体架构于2007差不多)。Excel中的数据多少将会影响客户端打开数据的速度。
Excel中的数据是由行*列来决定的,一个10列5000行的excel打开是在用户可以接受的范围内的,但是对excel做相关操作,比如“筛选”,可能会爆掉。
另外2003和2007有个最大的不同,2003所能存储216=65,536,而2007所能存储220=1,048,576

认识POI

Apache POI是Apache软件基金会的开放源码函式库,POI提供API给Java程序对Microsoft Office格式档案读和写的功能。针对2003和2007有两套不同的实现。
结构:

  • HSSF - 提供读写Microsoft Excel格式档案的功能。2003
  • XSSF - 提供读写Microsoft Excel OOXML格式档案的功能。 2007

POI实现Excel

我们用POI去实现Excel的导出功能,有两个性能问题:1. 导出的时间太长;2. 打开Excel的时间太长。你可以认为由于数据太大引起的性能问题,但是对于不可以避免的
数据量有可以解决的问题吗?就这两方面做一个浅浅的分析。

  1. 导出的时间

    a. 首先检查后台代码是否有可以优化的空间,过于复杂的SQL查询,多余的循环等等。如果你是一次性将所有结果放入List中,那么内存可能会逸出,可以考虑分页查询或者边查边写。

    b. 选择导出2003或2007,一般人可能会认为导出2007无疑是更好的选择,几乎没有行数的限制,版本高写入速度快。但是通过我的测试导出2003比2007快10倍,选择2003,行数的限制怎么办?

  2. 打开Excel的时间

    a. 当文件生成后,打开速度慢已经不是我们可以决定的了。之前说过,数据量影响打开的速度,去掉哪些可有可无的列

    b. 换台高配的电脑

如果想既不受行的限制,又能快速并查看导出,如何处理?

目前我能想到2种方案是:

  1. 以2003的方式导5000条记录为一个文件,最后打包成zip提供客户下载
  2. 因为Excel可以浏览CSV文件,可导到一个CSV文件中,注意英文环境下中文乱码的问题

英文操作系统,Excel打开csv乱码解决方案

首先需要了解CSV

逗号分隔值(Comma-Separated Values,CSV,有时也称为字符分隔值,因为分隔字符也可以不是逗号),其文件以纯文本形式存储表格数据(数字和文本)。纯文本意味着该文件是一个字符序列,不含必须像二进制数字那样被解读的数据。CSV文件由任意数目的记录组成,记录间以某种换行符分隔;每条记录由字段组成,字段间的分隔符是其它字符或字符串,最常见的是逗号或制表符。通常,所有记录都有完全相同的字段序列。

用记事本打开CSV,ANSI,UTF-8都不会出现乱码的,但是,CSV是用Excel打开utf-8 without BOM是会显示乱码的,这个尤其注意;用ANSI、UTF-8(有BOM)可以打开,为什么UTF-8(有BOM)却可以打开呢?知乎上有人是这么回答的,姑且就这么认为吧

UTF-8 不需要 BOM,尽管 Unicode 标准允许在 UTF-8 中使用 BOM。
所以不含 BOM 的 UTF-8 才是标准形式,在 UTF-8 文件中放置 BOM 主要是微软的习惯(顺便提一下:把带有 BOM 的小端序 UTF-16 称作「Unicode」而又不详细说明,这也是微软的习惯)

有上面可知,只要保证CSV文件是ANSI就可以了,在中文操作系统,文件格式指定GBK即可

在简体中文Windows操作系统中,ANSI 编码代表 GBK 编码;在繁体中文Windows操作系统中,ANSI编码代表Big5;在日文Windows操作系统中,ANSI 编码代表 Shift_JIS 编码

但是有一天客户跟我们说,他的英文操作系统打开CSV出现了乱码

这时候可以采用UTF-8(有BOM)存储CSV文件,在java中指定UTF-8都是有BOM,需要自己转换

public void utf2bom(File file) throws IOException {
        byte[] content = FileUtils.readFileToByteArray(file);

        byte[] bomContent = new byte[content.length + 3];
        bomContent[0] = (byte) 0xEF; 
        bomContent[1] = (byte) 0xBB; 
        bomContent[2] = (byte) 0xBF;

        System.arraycopy(content, 0, bomContent, 3, content.length);

        FileUtils.writeByteArrayToFile(file, bomContent);
}

客户端调用,这样你储存的文件就是UTF-8 with BOM,就不会受操作系统环境的影响了

File file = new File(excelDir + fullName);
OutputStreamWriter fwriter = new OutputStreamWriter(  
        new FileOutputStream(file), "UTF-8");
//转换utf-8 bom
utf2bom(file);        

CSV文件中包数字,转义

  • CSV用逗号隔开数据,用换行符产生一行一行的数据, 逗号就是第一个特殊字符,如果数据内容中出现了逗号,就要用半角双引把数据内容包起来,所以比如数据是 xilang,yan, 就要改成 “xilang,yan”。第二个特殊字符就是引号:”,数据中如果有引号,就要换成两个引号,比如xilang”yan要转义为xilang””yan。
  • 如果数据是存数字,并且第一个是0, 在excel下不会显示出来,解决方法就是,先用引号把数据包起来,再在数据前加一等号,比如:0123456就变成=”0123456″。但是这种解决方法有限,如果自己内容很长的话,也不能正确显示,不过还好,正常情况下,数字应该不会很长

Python面向对象(七)

在OOP程序设计中,当我们定义一个class的时候,可以从某个现有的class继承,新的class称为子类(Subclass),而被继承的class称为基类、父类或超类(Base class、Super class)

#任何类都是继承自object
class Person(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age


    def printPerson(self):
        print('%s is %d years old'%(self.name,self.age))

per = Person('Mike',54)
per.printPerson()

#类如何去实现继承关系
#一个学生是一个人,而且应该有成绩

#直接继承Person类,不实现任何方法
"""
class Student(Person):
    pass
"""

class Student(Person):
    def __init__(self, name, age, score):
        Person.__init__(self, name, age) #初始化父类构造器
        self.score = score
        self.__id = '123456'             #属性私有化

    def printScore(self):
        #Person.printPerson(self) #调用父类方法
        print('%s\'s score is %d' % (self.name,self.score))

    #getter setter方法

    def set_id(self, id):
        self.__id = id

    def get_id(self):
        return self.__id

stu = Student('Jim',13,98)   

stu.printPerson()
stu.printScore()

#外部可以访问属性
print(stu.score)

#print(stu.__id)
"""
访问私有变量会抛出异常
Traceback (most recent call last):
  File "oo.py", line 51, in <module>
    print(stu.__id)
AttributeError: 'Student' object has no attribute '__id'
"""
#设置值之前
print(stu.get_id())
stu.set_id('987654')
#设置值之后
print(stu.get_id())
小结

继承可以把父类的所有功能都直接拿过来,这样就不必重零做起,子类只需要新增自己特有的方法,也可以把父类不适合的方法覆盖重写。

Python文件操作(六)

在Python中读写文件,操作文件和目录是非常的方便的。操作文件的目录一部分放在io ,另一部分放在io.path,目录下

import os

print(os.path.abspath('.'))

#读文件
f = open('1.txt', 'r',encoding='utf-8')
str = f.read()
print(str)

#写文件

f.close()

f = open('1.txt','w',encoding='utf-8')
f.write('nb');
f.close()

os.rename('1.txt', '2.txt')

Cmd Markdown 简明语法手册

『Cmd 技术渲染的沙箱页面,点击此处编写自己的文档』

Cmd Markdown 简明语法手册

标签: Cmd-Markdown


1. 斜体和粗体

使用 * 和 ** 表示斜体和粗体。

示例:

这是 斜体,这是 粗体

2. 分级标题

使用 === 表示一级标题,使用 — 表示二级标题。

示例:

这是一个一级标题
============================

这是一个二级标题
--------------------------------------------------

### 这是一个三级标题

你也可以选择在行首加井号表示不同级别的标题 (H1-H6),例如:# H1, ## H2, ### H3,#### H4。

3. 外链接

使用 [描述](链接地址) 为文字增加外链接。

示例:

这是去往 本人博客 的链接。

4. 无序列表

使用 *,+,- 表示无序列表。

示例:

  • 无序列表项 一
  • 无序列表项 二
  • 无序列表项 三

5. 有序列表

使用数字和点表示有序列表。

示例:

  1. 有序列表项 一
  2. 有序列表项 二
  3. 有序列表项 三

6. 文字引用

使用 > 表示文字引用。

示例:

野火烧不尽,春风吹又生。

7. 行内代码块

使用 `代码` 表示行内代码块。

示例:

让我们聊聊 html

8. 代码块

使用 四个缩进空格 表示代码块。

示例:

这是一个代码块,此行左侧有四个不可见的空格。

9. 插入图像

使用 ![描述](图片链接地址) 插入图像。

示例:

我的头像

Cmd Markdown 高阶语法手册

1. 内容目录

在段落中填写 [TOC] 以显示全文内容的目录结构。

[TOC]

2. 标签分类

在编辑区任意行的列首位置输入以下代码给文稿标签:

标签: 数学 英语 Markdown

或者

Tags: 数学 英语 Markdown

3. 删除线

使用 ~~ 表示删除线。

~~这是一段错误的文本。~~

4. 注脚

使用 [^keyword] 表示注脚。

这是一个注脚1的样例。

这是第二个注脚2的样例。

5. LaTeX 公式

$ 表示行内公式:

质能守恒方程可以用一个很简洁的方程式 $E=mc^2$ 来表达。

$$ 表示整行公式:

$$sum_{i=1}^n a_i=0$$

$$f(x_1,x_x,ldots,x_n) = x_1^2 + x_2^2 + cdots + x_n^2 $$

$$sum^{j-1}{k=0}{widehat{gamma}{kj} z_k}$$

访问 MathJax 参考更多使用方法。

6. 加强的代码块

支持四十一种编程语言的语法高亮的显示,行号显示。

非代码示例:

$ sudo apt-get install vim-gnome

Python 示例:

@requires_authorization
def somefunc(param1='', param2=0):
    '''A docstring'''
    if param1 > param2: # interesting
        print 'Greater'
    return (param2 - param1 + 1) or None

class SomeClass:
    pass

>>> message = '''interpreter
... prompt'''

JavaScript 示例:

/**
* nth element in the fibonacci series.
* @param n >= 0
* @return the nth element, >= 0.
*/
function fib(n) {
  var a = 1, b = 1;
  var tmp;
  while (--n >= 0) {
    tmp = a;
    a += b;
    b = tmp;
  }
  return a;
}

document.write(fib(10));

7. 流程图

示例

st=>start: Start:>https://www.zybuluo.com
io=>inputoutput: verification
op=>operation: Your Operation
cond=>condition: Yes or No?
sub=>subroutine: Your Subroutine
e=>end

st->io->op->cond
cond(yes)->e
cond(no)->sub->io

更多语法参考:流程图语法参考

8. 序列图

示例 1

Alice->Bob: Hello Bob, how are you?
Note right of Bob: Bob thinks
Bob-->Alice: I am good thanks!

示例 2

Title: Here is a title
A->B: Normal line
B-->C: Dashed line
C->>D: Open arrow
D-->>A: Dashed open arrow

更多语法参考:序列图语法参考

9. 表格支持

示例:

项目 价格 数量
计算机 $1600 5
手机 $12 12
管线 $1 234

10. 定义型列表

名词 1
定义 1(左侧有一个可见的冒号和四个不可见的空格)
代码块 2

这是代码块的定义(左侧有一个可见的冒号和四个不可见的空格)

代码块(左侧有八个不可见的空格)

11. Html 标签

本站支持在 Markdown 语法中嵌套 Html 标签,譬如,你可以用 Html 写一个纵跨两行的表格:

<table>
    <tr>
        <th rowspan="2">值班人员</th>
        <th>星期一</th>
        <th>星期二</th>
        <th>星期三</th>
    </tr>
    <tr>
        <td>李强</td>
        <td>张明</td>
        <td>王平</td>
    </tr>
</table>
值班人员 星期一 星期二 星期三
李强 张明 王平

12. 内嵌图标

本站的图标系统对外开放,在文档中输入

<i class="icon-weibo"></i>

即显示微博的图标:

替换 上述 i 标签 内的 icon-weibo 以显示不同的图标,例如:

<i class="icon-renren"></i>

即显示人人的图标:

更多的图标和玩法可以参看 font-awesome 官方网站。

13. 待办事宜 Todo 列表

使用带有 [ ] 或 [x] (未完成或已完成)项的列表语法撰写一个待办事宜列表,并且支持子列表嵌套以及混用Markdown语法,例如:

- [ ] **Cmd Markdown 开发**
    - [ ] 改进 Cmd 渲染算法,使用局部渲染技术提高渲染效率
    - [ ] 支持以 PDF 格式导出文稿
    - [x] 新增Todo列表功能 [语法参考](https://github.com/blog/1375-task-lists-in-gfm-issues-pulls-comments)
    - [x] 改进 LaTex 功能
        - [x] 修复 LaTex 公式渲染问题
        - [x] 新增 LaTex 公式编号功能 [语法参考](http://docs.mathjax.org/en/latest/tex.html#tex-eq-numbers)
- [ ] **七月旅行准备**
    - [ ] 准备邮轮上需要携带的物品
    - [ ] 浏览日本免税店的物品
    - [x] 购买蓝宝石公主号七月一日的船票

对应显示如下待办事宜 Todo 列表:

  • [ ] Cmd Markdown 开发
    • [ ] 改进 Cmd 渲染算法,使用局部渲染技术提高渲染效率
    • [ ] 支持以 PDF 格式导出文稿
    • [x] 新增Todo列表功能 语法参考
    • [x] 改进 LaTex 功能
      • [x] 修复 LaTex 公式渲染问题
      • [x] 新增 LaTex 公式编号功能 语法参考
  • [ ] 七月旅行准备
    • [ ] 准备邮轮上需要携带的物品
    • [ ] 浏览日本免税店的物品
    • [x] 购买蓝宝石公主号七月一日的船票

  1. 这是一个 注脚文本。 

  2. 这是另一个 注脚文本。 

cxf生成客户端代码成功,使用抛出异常

今天用cxf生成客户端代码,通过wsdl获取天气预报

wsdl:http://webservice.webxml.com.cn/WebServices/WeatherWS.asmx?wsdl

参照博客http://a.qt.hk/v/Csdnblog/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcG9zb25yaWNrL2FydGljbGUvZGV0YWlscy80NTU3NDY2Mw==

一直报错,折腾了好久

Exception in thread "main" javax.xml.ws.soap.SOAPFaultException: No namespace on "html" element. You must send a SOAP request.
at org.apache.cxf.jaxws.JaxWsClientProxy.invoke(JaxWsClientProxy.java:156)
at com.sun.proxy.$Proxy32.getWeather(Unknown Source)
at com.devins.ws.weather.WeatherQueryTest.main(WeatherQueryTest.java:18)
Caused by: org.apache.cxf.binding.soap.SoapFault: No namespace on "html" element. You must send a SOAP request.
at org.apache.cxf.binding.soap.interceptor.ReadHeadersInterceptor.readVersion(ReadHeadersInterceptor.java:109)
at org.apache.cxf.binding.soap.interceptor.ReadHeadersInterceptor.handleMessage(ReadHeadersInterceptor.java:142)
at org.apache.cxf.binding.soap.interceptor.ReadHeadersInterceptor.handleMessage(ReadHeadersInterceptor.java:60)
at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:263)
at org.apache.cxf.endpoint.ClientImpl.onMessage(ClientImpl.java:801)
at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponseInternal(HTTPConduit.java:1679)
at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponse(HTTPConduit.java:1517)
at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.close(HTTPConduit.java:1425)
at org.apache.cxf.transport.AbstractConduit.close(AbstractConduit.java:56)
at org.apache.cxf.transport.http.HTTPConduit.close(HTTPConduit.java:650)

注意,此客户端工程调用无需引入cxf的相关jar包,引入执行上面的测试代码会报如上错误: