Java-报表技术1-POI:https://blog.csdn.net/weixin_43994244/article/details/126227702
//日期格式化
private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
/**
* 导出用户详细信息--使用模板
* @param id
* @param response
* @throws Exception
*/
public void downLoadUserInfoByTemplate(Long id, HttpServletResponse response) throws Exception {
//1.读取模板
//获取项目的根目录,创建目录---获取绝对路径
File rootPath = new File(ResourceUtils.getURL("classpath:").getPath());
File templateFile = new File(rootPath.getAbsolutePath(), "/excel_template/userInfo.xlsx");
//创建有模板的工作簿
Workbook workbook = new XSSFWorkbook(templateFile);
Sheet sheet = workbook.getSheetAt(0);
//2.获取用户数据
User user = userMapper.selectByPrimaryKey(id);
//3.数据放入模板---存入哪一行哪个单元格
//用户名 第2行第2列
sheet.getRow(1).getCell(1).setCellValue(user.getUserName());
//手机号 第3行第2列
sheet.getRow(2).getCell(1).setCellValue(user.getPhone());
//生日 第4行第2列
sheet.getRow(3).getCell(1).setCellValue(simpleDateFormat.format(user.getBirthday()));
//工资 第5行第2列
sheet.getRow(4).getCell(1).setCellValue(user.getSalary());
//入职日期 第6行第2列
sheet.getRow(5).getCell(1).setCellValue(simpleDateFormat.format(user.getHireDate()));
//省份 第7行第2列
sheet.getRow(6).getCell(1).setCellValue(user.getProvince());
//城市 第7行第4列
sheet.getRow(6).getCell(3).setCellValue(user.getCity());
//现住址 第8行第1列---合并单元格
sheet.getRow(7).getCell(1).setCellValue(user.getAddress());
//司龄 第6行第4列---导出公式(入职时间到现在)
// --填入excel的公式=CONCATENATE(DATEDIF(B6,TODAY(),"Y"),"年",DATEDIF(B6,TODAY(),"YM"),"个月")
sheet.getRow(5).getCell(3).setCellFormula("CONCATENATE(DATEDIF(B6,TODAY(),\"Y\"),\"年\",DATEDIF(B6,TODAY(),\"YM\"),\"个月\")");
//图片处理开始
//照片--第二行到第五行,第三列到第四列--合并单元格
String userPhoto = user.getPhoto();
//先创建一个字节输出流
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
//优化,获取拓展名,根据不同拓展名
String extName = userPhoto.substring(userPhoto.lastIndexOf(".") + 1).toUpperCase();
//读取图片,放入一个带有缓冲区的图片类中,主要作用是将一副图片加载到内存中
BufferedImage bufferedImage = ImageIO.read(new File(rootPath+user.getPhoto()));
//把图片写入到字节输出流中
ImageIO.write(bufferedImage,extName,outputStream);
//Patriarch图片写入
Drawing patriarch = sheet.createDrawingPatriarch();
//ClientAnchor指定图片位置--后四起始列,起始行,结束列,结束行,前四个参数,左上角向x,y偏离多少,右下角向x,y偏离多少(偏移单位-1cm=36w) 不占满表格
ClientAnchor clientAnchor = new XSSFClientAnchor(0, 0, 0, 0, 2, 1, 4, 5);
//优化
int format = 0;
switch (extName){
case "JPG":{
format = XSSFWorkbook.PICTURE_TYPE_JPEG;
} case "JPEG":{
format = XSSFWorkbook.PICTURE_TYPE_JPEG;
} case "PNG":{
format = XSSFWorkbook.PICTURE_TYPE_PNG;
}
}
//开始把图片写入到sheet指定的位置
patriarch.createPicture(clientAnchor,workbook.addPicture(outputStream.toByteArray(),format));
//图片处理结束
//4.下载
//一个流两个头
String fileName = "员工("+user.getUserName()+")详细数据.xlsx";
response.setHeader("content-disposition","attachment;filename="+new String(fileName.getBytes(),"ISO8859-1"));
response.setContentType("application/vnd.ms-excel");
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
//执行
workbook.write(response.getOutputStream());
}
前提:导出代码时,必须要提前知道要导出数据在哪一行哪一个单元格,但是模板一旦发生调整,java代码也要修改,所以可以自定义引擎,有了引擎即使模板修改了java代码也不用修改。
思路:在制作模板时,在需要插入数据的位置做上标记,在导出时,对象的属性和标记做对应,如果对应匹配一样,就把值赋值到相应的位置。
模板:
工具类: bean和map转换
可以借用hutool的BeanUtil里的方法:
此处是用jdk的API编写:
public class EntityUtils {
private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
/**
* 实体类转Map
* @param object
* @return
*/
public static Map<String, Object> entityToMap(Object object) {
Map<String, Object> map = new HashMap();
for (Field field : object.getClass().getDeclaredFields()){
try {
boolean flag = field.isAccessible();
field.setAccessible(true);
Object o = field.get(object);
if(o instanceof Date){
o = sdf.format(o);
}
map.put(field.getName(), o);
field.setAccessible(flag);
} catch (Exception e) {
e.printStackTrace();
}
}
return map;
}
/**
* Map转实体类
* @param map 需要初始化的数据,key字段必须与实体类的成员名字一样,否则赋值为空
* @param entity 需要转化成的实体类
* @return
*/
public static <T> T mapToEntity(Map<String, Object> map, Class<T> entity) {
T t = null;
try {
t = entity.newInstance();
for(Field field : entity.getDeclaredFields()) {
if (map.containsKey(field.getName())) {
boolean flag = field.isAccessible();
field.setAccessible(true);
Object object = map.get(field.getName());
if (object!= null && field.getType().isAssignableFrom(object.getClass())) {
field.set(t, object);
}
field.setAccessible(flag);
}
}
return t;
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return t;
}
}
public class ExcelExportEngine {
private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
public static Workbook writeToExcel(Object object,Workbook workbook,String photoPath) throws Exception {
//把bean转成map
Map<String, Object> map = EntityUtils.entityToMap(object);
Sheet sheet = workbook.getSheetAt(0);
Row row = null;
Cell cell = null;
//循环100行,每一行循环100个单元格---定位到具体的cell
for (int i = 0; i < 10; i++) {
row = sheet.getRow(i);
if(row == null){
break;}
else {
for (int j = 0; j < 10; j++) {
cell = row.getCell(j);
if(cell != null){
//比较单元格中的值,是否和map中的key一致,如果一致向单元格中放入map这个key对应的值
writeToCell(cell,map);
}
}
}
}
//处理图片
if(StringUtils.isNotBlank(photoPath)){
//File rootPath = new File(ResourceUtils.getURL("classpath:").getPath()); //SpringBoot项目获取根目录的方式
ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();
//BufferedImage是一个带缓冲区图像类,主要作用是将一幅图片加载到内存中
BufferedImage bufferImg = ImageIO
.read(new File(photoPath));
ImageIO.write(bufferImg, "jpg", byteArrayOut);
Drawing patriarch = sheet.createDrawingPatriarch();
//获取模板图片位置
Sheet sheet2 = workbook.getSheetAt(1);
row = sheet2.getRow(0);
int col1 = ((Double) row.getCell(0).getNumericCellValue()).intValue();
int row1 = ((Double) row.getCell(1).getNumericCellValue()).intValue();
int col2 = ((Double) row.getCell(2).getNumericCellValue()).intValue();
int row2 = ((Double) row.getCell(3).getNumericCellValue()).intValue();
//锚点,固定点
ClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, col1, row1, col2, row2);
patriarch.createPicture(anchor, workbook.addPicture(byteArrayOut.toByteArray(), Workbook.PICTURE_TYPE_JPEG));
//删除第二个sheet
workbook.removeSheetAt(1);
}
return workbook;
}
//比较单元格中的值,是否和map中的key一致,如果一致向单元格中放入map这个key对应的值
private static void writeToCell(Cell cell, Map<String, Object> map) {
CellType cellType = cell.getCellType();
//公式的话不处理
switch(cellType){
case FORMULA:{
break;
} default:{
String cellValue = cell.getStringCellValue();
if(StringUtils.isNotBlank(cellValue)){
//System.out.println("获取到的cellValue"+cellValue);
for (String key : map.keySet()){
if(key.equals(cellValue)){
cell.setCellValue(map.get(key).toString());
//System.out.println("获取到的value:"+map.get(key).toString());
}
}
}
}
}
}
}
/**
* 导出用户详细信息--使用模板引擎
* @param id
* @param response
* @throws Exception
*/
public void downLoadUserInfoByTemplate2(Long id, HttpServletResponse response) throws Exception {
//1.读取模板
//获取项目的根目录,创建目录---获取绝对路径
File rootPath = new File(ResourceUtils.getURL("classpath:").getPath());
File templateFile = new File(rootPath.getAbsolutePath(), "/excel_template/userInfo2.xlsx");
//创建有模板的工作簿
Workbook workbook = new XSSFWorkbook(templateFile);
Sheet sheet = workbook.getSheetAt(0);
//2.获取用户数据
User user = userMapper.selectByPrimaryKey(id);
System.out.println("获取到的用户信息"+user.toString());
//3.通过自定义引擎放入数据--图片的绝对路径
workbook = ExcelExportEngine.writeToExcel(user, workbook,rootPath.getPath()+user.getPhoto());
patriarch.createPicture(clientAnchor,workbook.addPicture(outputStream.toByteArray(),format));
//图片处理结束
//4.下载
//一个流两个头
String fileName = "员工("+user.getUserName()+")详细数据.xlsx";
response.setHeader("content-disposition","attachment;filename="+new String(fileName.getBytes(),"ISO8859-1"));
//xls
//response.setContentType("application/vnd.ms-excel");
//xlsx
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
//执行
workbook.write(response.getOutputStream());
}
用户模式: 用户模式有许多封装好的方法操作简单,但创建太多的对象,非常耗内存(之前使用的方法)
事件模式: 基于SAX方式解析XML,SAX全称Simple API for XML,它是一个接口,也是一个软件包。它是一种XML解析的替代方法,不同于DOM解析XML文档时把所有内容一次性加载到内存中的方式,它逐行扫描文档,一边扫描,一边解析。
SXSSF对象:是用来生成海量excel数据文件,主要原理是借助临时存储空间生成excel
弊端:
1、不能使用模板
2、不能使用太多的样式
实现方式:
1.dom4j:一次性加载xml文件再解析
2.SAX:逐行加载,逐行解析
3.SXSSWorkBook:一旦内存中的对象的个数达到指定值时,就将内存中的这些对象的内容写入到磁盘中,就可以将这些对象从内存中销毁,直至excel导出完成。
public void downLoadMillion(HttpServletResponse response) throws Exception {
//使用sax方式解析
Workbook workbook = new SXSSFWorkbook();
//导出500w条数据,一个sheet只能放100w行
//记录了处理了多少条数据
int number = 0;
//分页查询
int page = 1;
//记录row的行索引
int rowIndex = 1;
//创建行
Row row = null;
Sheet sheet = null;
while (true) {
List<User> userList = this.findPage(page, 1000000);
if (CollectionUtils.isEmpty(userList)) {
break;//数据为空不再查询
}
if (number % 1000000 == 0) {
//表示应该创建新的标题
sheet = workbook.createSheet("第" + (number/1000000)+ "个sheet表");
//重置rowIndex
rowIndex = 1;
//设置列宽
sheet.setColumnWidth(0,8*256);
sheet.setColumnWidth(1,12*256);
sheet.setColumnWidth(2,15*256);
sheet.setColumnWidth(3,15*256);
sheet.setColumnWidth(4,30*256);
//设置小标题
String[] title = new String[]{"编号", "姓名", "手机号", "入职日期", "现住址"};
//创建标题行
Row titleRow = sheet.createRow(0);
for (int i = 0; i < 5; i++) {
titleRow.createCell(i).setCellValue(title[i]);
}
}
//向excel中添加内容
for (User user : userList) {
row = sheet.createRow(rowIndex);
row.createCell(0).setCellValue(user.getId());
row.createCell(1).setCellValue(user.getUserName());
row.createCell(2).setCellValue(user.getPhone());
row.createCell(3).setCellValue(simpleDateFormat.format(user.getHireDate()));
row.createCell(4).setCellValue(user.getAddress());
rowIndex++;
number++;
}
page++;//下一页
}
//4.下载
//一个流两个头
String fileName = "百万数据导出.xlsx";
response.setHeader("content-disposition","attachment;filename="+new String(fileName.getBytes(),"ISO8859-1"));
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
//执行
workbook.write(response.getOutputStream());
}
用户模式: 加载并读取Excel时,是通过一次性的将所有数据加载到内存中再去解析每个单元格内容。当Excel数据量较大时,由于不同的运行环境可能会造成内存不足甚至OOM异常。
事件模式: 它逐行扫描文档,一边扫描一边解析。由于应用程序只是在读取数据时检查数据,因此不需要将数据存储在内存中,这对于大型文档的解析是个巨大优势。
1.设置POI的事件模式
2.Sax解析
package com.itheima.test;
import com.itheima.pojo.User;
import org.apache.poi.xssf.eventusermodel.XSSFSheetXMLHandler;
import org.apache.poi.xssf.usermodel.XSSFComment;
public class SheetHandler implements XSSFSheetXMLHandler.SheetContentsHandler {
// 编号 用户名 手机号 入职日期 现住址
private User user=null;
@Override
public void startRow(int rowIndex) { //每一行的开始 rowIndex代表的是每一个sheet的行索引
if(rowIndex==0){
user = null;
}else{
user = new User();
}
}
@Override //处理每一行的所有单元格
public void cell(String cellName, String cellValue, XSSFComment comment) {
if(user!=null){
String letter = cellName.substring(0, 1); //每个单元名称的首字母 A B C
switch (letter){
case "A":{
user.setId(Long.parseLong(cellValue));
break;
}
case "B":{
user.setUserName(cellValue);
break;
}
}
}
}
@Override
public void endRow(int rowIndex) { //每一行的结束
if(rowIndex!=0){
System.out.println(user);
}
}
}
package com.itheima.test;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.opc.PackageAccess;
import org.apache.poi.xssf.eventusermodel.XSSFReader;
import org.apache.poi.xssf.eventusermodel.XSSFSheetXMLHandler;
import org.apache.poi.xssf.model.SharedStringsTable;
import org.apache.poi.xssf.model.StylesTable;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;
import java.io.InputStream;
/**
* 自定义Excel解析器
*/
public class ExcelParser {
public void parse (String path) throws Exception {
//1.根据Excel获取OPCPackage对象
OPCPackage pkg = OPCPackage.open(path, PackageAccess.READ);
try {
//2.创建XSSFReader对象
XSSFReader reader = new XSSFReader(pkg);
//3.获取SharedStringsTable对象
SharedStringsTable sst = reader.getSharedStringsTable();
//4.获取StylesTable对象
StylesTable styles = reader.getStylesTable();
XMLReader parser = XMLReaderFactory.createXMLReader();
// 处理公共属性:Sheet名,Sheet合并单元格
parser.setContentHandler(new XSSFSheetXMLHandler(styles,sst, new SheetHandler(), false));
XSSFReader.SheetIterator sheets = (XSSFReader.SheetIterator) reader.getSheetsData();
while (sheets.hasNext()) {
InputStream sheetstream = sheets.next();
InputSource sheetSource = new InputSource(sheetstream);
try {
parser.parse(sheetSource);
} finally {
sheetstream.close();
}
}
} finally {
pkg.close();
}
}
}
用户模式下读取测试Excel文件直接内存溢出,测试Excel文件映射到内存中还是占用了不少内存;事件模式下可以流畅的运行。
public class POIDemo5 {
public static void main(String[] args) throws Exception{
new ExcelParser().parse("C:\\Users\\syl\\Desktop\\百万用户数据的导出.xlsx");
}
}
CSV文件:Comma-Separated Values,中文叫逗号分隔值或者字符分割值,其文件以纯文本的形式存储表格数据。该文件是一个字符序列,可以由任意数目的记录组成,记录间以某种换行符分割。每条记录由字段组成,字段间的分隔符是其他字符或者字符串。所有的记录都有完全相同的字段序列,相当于一个结构化表的纯文本形式。
用文本文件、excel或者类似与文本文件的编辑器都可以打开CSV文件。
使用opencsv类库来导出csv文
依赖:
<dependency>
<groupId>com.opencsvgroupId>
<artifactId>opencsvartifactId>
<version>4.5version>
dependency>
opencsv常用API:
写入到csv文件会用到CSVWriter对象,创建此对象常见API如下:
CSVWriter(Writer writer);
CSVWriter(Writer writer, char separator);
CSVWriter(Writer writer, char separator, char quotechar);
构造器涉及到的三个参数:
CSVWriter.DEFAULT_SEPARATOR
用于分割各列。使用CSVWriter对象写入数据常用的方法如下:
writerAll(List
writerNext(String… nextLine);
读取csv文件会用到CSVReader对象,创建此对象常见API如下:
CSVReader(Reader reader)
CSVReader(Reader reader, char separator)
CSVReader(Reader reader, char separator, char quotechar)
/**
* 导出百万数据到CSV文件
* @param response
* @throws Exception
*/
public void downLoadCSV(HttpServletResponse response) throws Exception {
ServletOutputStream outputStream = response.getOutputStream();
//导出
String fileName = "csv百万数据导出.csv";
response.setHeader("content-disposition","attachment;filename="+new String(fileName.getBytes(),"ISO8859-1"));
response.setContentType("text/csv");
//写文件
CSVWriter csvWriter = new CSVWriter(new OutputStreamWriter(outputStream,"UTF-8"));
//写第一行
//设置小标题
String[] title = new String[]{"编号", "姓名", "手机号", "入职日期", "现住址"};
csvWriter.writeNext(title);
int page = 1;
while (true) {
List<User> userList = this.findPage(1, 2000000);
if(CollectionUtils.isEmpty(userList)){
break;
}for(User user : userList){
csvWriter.writeNext(new String[]{user.getId().toString(),user.getUserName(),user.getPhone(),simpleDateFormat.format(user.getHireDate()),user.getAddress()});
}
page++;
csvWriter.flush();
}
csvWriter.close();
}
import com.opencsv.CSVReader;
import java.io.FileReader;
import java.text.SimpleDateFormat;
import java.time.Year;
import java.util.List;
//读取百万级数据的csv文件
public class CsvDemo {`在这里插入代码片`
private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
public static void main(String[] args) throws Exception {
CSVReader csvReader = new CSVReader(new FileReader("d:\\百万用户数据的导出.csv"));
String[] titles = csvReader.readNext(); //读取到第一行 是小标题
// "编号","姓名","手机号","入职日期","现住址"
User user = null;
while (true){
user = new User();
String[] content = csvReader.readNext();
if(content==null){
break;
}
user.setId(Long.parseLong(content[0]));
user.setUserName(content[1]);
user.setPhone(content[2]);
user.setHireDate(simpleDateFormat.parse(content[3]));
user.setAddress(content[4]);
System.out.println(user);
}
}
}
XWPFDocument代表一个docx文档,其可以用来读docx文档,也可以用来写docx文档
一个文档包含多个段落,一个段落包含多个Runs文本,一个Runs包含多个Run,Run是文档的最小单元
获取所有段落:List paragraphs = word.getParagraphs();
获取一个段落中的所有片段Runs:List xwpfRuns = xwpfParagraph.getRuns();
获取一个Runs中的一个Run:XWPFRun run = xwpfRuns.get(index);
2、poi操作word中的表格
一个文档包含多个表格,一个表格包含多行,一行包含多列单元格
获取所有表格:List xwpfTables = doc.getTables();
获取一个表格中的所有行:List xwpfTableRows = xwpfTable.getRows();
获取一行中的所有列:List xwpfTableCells = xwpfTableRow.getTableCells();
获取一格里的内容:List paragraphs = xwpfTableCell.getParagraphs();
之后和正文段落一样
制作一个word模板,把动态的内容先写特殊字符然后替换,表格自己创建然后向表格中放内容
1.准备方法:一个是想指定的单元格中放入图片,另一个是 复制word中表格的行
/**
* 向单元格写入图片
*/
private void setCellImage(XWPFTableCell cell, File imageFile) {
XWPFRun run = cell.getParagraphs().get(0).createRun();
//流会自动关闭
try(FileInputStream inputStream = new FileInputStream(imageFile)) {
//输入流,图片类型,文件名,高度,宽度(图片扩大倍数)
run.addPicture(inputStream,XWPFDocument.PICTURE_TYPE_JPEG,imageFile.getName(), Units.toEMU(100), Units.toEMU(50));
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 用户深复制行
*/
private void copyRow(XWPFTable table, XWPFTableRow sourceRow, int rowIndex) {
XWPFTableRow targetRow = table.insertNewTableRow(rowIndex);
//设置行属性
targetRow.getCtRow().setTrPr(sourceRow.getCtRow().getTrPr());
//获取源行的单元格--设置单元格属性
List<XWPFTableCell> cells = sourceRow.getTableCells();
//
if(CollectionUtils.isEmpty(cells)){
return;
}
XWPFTableCell targetCell = null;
for (XWPFTableCell cell : cells) {
targetCell = targetRow.addNewTableCell();
//附上单元格的样式
//单元格的样式
targetCell.getCTTc().setTcPr(cell.getCTTc().getTcPr());
//单元个中段落属性
targetCell.getParagraphs().get(0).getCTP().setPPr(cell.getParagraphs().get(0).getCTP().getPPr());
}
}
2.下载用户合同
/**
* 下载用户合同
* @param id
*/
public void downloadContract(Long id,HttpServletResponse response) throws Exception {
//1.读取到模板
//获取项目的根目录,创建目录---获取绝对路径
File rootPath = new File(ResourceUtils.getURL("classpath:").getPath());
File templateFile = new File(rootPath.getAbsolutePath(), "word_template/contract_template.docx");
XWPFDocument document = new XWPFDocument(new FileInputStream(templateFile));
//2.查询当前用户--->map方便操作
User user = this.findById(id);
HashMap<String, String> hashMap = new HashMap<>();
hashMap.put("userName",user.getUserName());
hashMap.put("hireDate",simpleDateFormat.format(user.getHireDate()));
hashMap.put("address",user.getAddress());
//3.1.替换数据--处理正文
List<XWPFParagraph> paragraphs = document.getParagraphs();
for (XWPFParagraph paragraph : paragraphs) {
List<XWPFRun> runs = paragraph.getRuns();
for (XWPFRun run : runs) {
String text = run.getText(0);
//循环遍历是否包含map的key
for (String key : hashMap.keySet()) {
if(text.contains(key)){
//如果包含,替换掉
run.setText(text.replaceAll(key,hashMap.get(key)),0);
}
}
}
}
//3.2.表格数据
List<Resource> resources = user.getResourceList();
//获取表格
XWPFTable table = document.getTables().get(0);
//获取模板的第一行
XWPFTableRow row = table.getRow(0);
//存放东西,向表格中添加行
int rowIndex = 1;
for (Resource resource : resources) {
//添加行
//table.addRow(row);
copyRow(table,row,rowIndex);
XWPFTableRow tableRow = table.getRow(rowIndex);
tableRow.getCell(0).setText(resource.getName());
tableRow.getCell(1).setText(resource.getPrice().toString());
tableRow.getCell(2).setText(resource.getNeedReturn()?"需要":"不需要");
//照片处理
File imageFile = new File(rootPath, "/static"+resource.getPhoto());
setCellImage(tableRow.getCell(3),imageFile);
rowIndex++;
}
//4.导出word
String fileName = "员工"+user.getUserName()+"合同.docx";
response.setHeader("content-disposition","attachment;filename="+new String(fileName.getBytes(),"ISO8859-1"));
response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
document.write(response.getOutputStream());
}