简单OA项目笔记(5):生成、上载excel格式的动态表格(POI)

简单OA项目笔记(5):生成、下载excel格式的动态表格(POI)


把动态表格生成excel用的库叫POI,从数据库提取出数据之后,利用POI可以产生一个stream。


大体步骤:

步骤一:从数据库读信息
步骤二:数据组装成excel
步骤三:用InputStream发回浏览器



数据流:

1.struts.xml中action配置

result:是“stream”
vnd.ms-excel:文件类型是tomcat提供的
"contentDisposition">filename="AllUsers.xls":配置下载全都是附件形式,文件名以配置为准
"inputName">downloadFile:需要具体action中的get“downloadFile”

		<action name="generateExcel" class="generateExcelAction">
			<result name="success" type="stream"><!-- result是“stream”-->
				<param name="contentType">application/vnd.ms-excel</param> <!--文件类型是tomcat提供的-->
				<param name="contentDisposition">attachment;filename="AllUsers.xls"</param> <!--配置下载全都是附件形式,文件名以配置为准-->
				<param name="inputName">downloadFile</param> <!--需要具体action中的get“downloadFile”-->
			</result>
		</action>




2.GenerateExcelAction

这个getter和上边"inputName">downloadFile 一致

	public InputStream getDownloadFile()
	{
		return this.service.getInputStream();
	}



3.UserServiceImpl

这里边是生成excel的核心代码,组装好之后,要把生成的excel转换成InputStream类型还给action。action根据下载配置,把文件用附件的形式发给客户端,完成下载。

生成excel的过程是
1.创建sheet
2.由sheet创建:行
3.表头:short表示0号格
设置中文
4.迭代创建表格:
5.把表格写进 ByteArrayOutputStream 对象,再转换成InputStream 对象返还给action

	public InputStream getInputStream()
	{
		HSSFWorkbook wb = new HSSFWorkbook();
		HSSFSheet sheet = wb.createSheet("sheet1");

		HSSFRow row = sheet.createRow(0);

		HSSFCell cell = row.createCell((short) 0);
		cell.setEncoding(HSSFCell.ENCODING_UTF_16);
		cell.setCellValue("序号");

		cell = row.createCell((short) 1);
		cell.setEncoding(HSSFCell.ENCODING_UTF_16);
		cell.setCellValue("姓");

		cell = row.createCell((short) 2);
		cell.setEncoding(HSSFCell.ENCODING_UTF_16);
		cell.setCellValue("名");

		cell = row.createCell((short) 3);
		cell.setEncoding(HSSFCell.ENCODING_UTF_16);
		cell.setCellValue("年龄");

		List<User> list = this.findAll();

		for (int i = 0; i < list.size(); ++i)
		{
			User user = list.get(i);

			row = sheet.createRow(i + 1);

			cell = row.createCell((short) 0);
			cell.setEncoding(HSSFCell.ENCODING_UTF_16);
			cell.setCellValue(i + 1);

			cell = row.createCell((short) 1);
			cell.setEncoding(HSSFCell.ENCODING_UTF_16);
			cell.setCellValue(user.getFirstname());

			cell = row.createCell((short) 2);
			cell.setEncoding(HSSFCell.ENCODING_UTF_16);
			cell.setCellValue(user.getLastname());

			cell = row.createCell((short) 3);
			cell.setEncoding(HSSFCell.ENCODING_UTF_16);
			cell.setCellValue(user.getAge());
		}
		
		ByteArrayOutputStream os = new ByteArrayOutputStream();
		
		try
		{
			wb.write(os);
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}
		
		byte[] content = os.toByteArray();
		
		InputStream is = new ByteArrayInputStream(content);
		
		return is;
		
	}







4.用另一种思路:产生临时文件

上边产生excel的的方式没有用到临时文件,直接在内存里就都完成了。还有一种方法是利用临时文件保存excel表信息。

这种方法比较麻烦,首先是下载的文件名不能重复,否则多线程情况下就该互相覆盖掉了

String fileName = RandomStringUtils.randomAlphanumeric(10);

		fileName = new StringBuffer(fileName).append(".xls").toString();

		final File file = new File(fileName);

		try
		{
			OutputStream os = new FileOutputStream(file);
			wb.write(os);
			os.close();
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}

		InputStream is = null;
		try
		{
			is = new FileInputStream(file);
		}
		catch (FileNotFoundException e)
		{
			e.printStackTrace();
		}

		new Thread(new Runnable()
		{
			public void run()
			{
				try
				{
					Thread.sleep(15000);
				}
				catch (InterruptedException e)
				{
					e.printStackTrace();
				}
				
				file.delete();//删除临时文件 
			}
		}).start();

		return is;

而且产生的随机文件名的临时文件,还要自己估摸一个时间延迟删除:

		new Thread(new Runnable()
		{
			public void run()
			{
				try
				{
					Thread.sleep(15000);
				}
				catch (InterruptedException e)
				{
					e.printStackTrace();
				}
				
				file.delete();//删除临时文件 
			}
		}).start();

如果没删掉还有靠其他方法比如servlet启动时删除:

	public void init() throws ServletException
	{	
		File file = new File(".");
		
		File[] subFiles = file.listFiles(new FileFilter()
		{
			public boolean accept(File pathname)
			{
				if(pathname.getName().endsWith("xls"))
				{
					return true;
				}
				
				return false;
			}
		}
		);
		
		for(File f : subFiles)
		{
			f.delete();
		}
	}


这个比较恶心,目前来看还是用第一种说的 在内存里 ByteArrayOutputStream 完成吧,内存很紧张的时候可能才会用临时文件吧。