三、项目拓展
下面在前面的小项目中添加一个小功能,动态生成execl文件及下载,要用到apache的POI组件,这个包默认已经被spring加载
1、在UserService.java中添加方法
public InputStream getInputStream();
在它的实现类中实现
package com.test.service.impl;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import com.test.bean.User;
import com.test.dao.UserDAO;
import com.test.service.UserService;
public class UserServiceImpl implements UserService {
private UserDAO userDao;
public void delete(User user) {
// TODO Auto-generated method stub
this.userDao.removeUser(user);
}
public List<User> findAll() {
// TODO Auto-generated method stub
return this.userDao.findAllUsers();
}
public User findById(Integer id) {
// TODO Auto-generated method stub
return this.userDao.findUserById(id);
}
public void save(User user) {
// TODO Auto-generated method stub
this.userDao.saveUser(user);
}
public void update(User user) {
// TODO Auto-generated method stub
this.userDao.updateUser(user);
}
public UserDAO getUserDao() {
return userDao;
}
public void setUserDao(UserDAO userDao) {
this.userDao = userDao;
}
public InputStream getInputStream()
{
HSSFWorkbook wb = new HSSFWorkbook(); //生成ececl
HSSFSheet sheet = wb.createSheet("sheet1"); //创建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());
}
File file = new File("test.xls");
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();
}
return is;
}
}
生产的临时文件test.xls在apache的bin目录中
2、在com.test.user.action中新建GenerateExcelAction.java
package com.test.action.user;
import java.io.InputStream;
import com.opensymphony.xwork2.ActionSupport;
import com.test.service.UserService;
public class GenerateExcelAction extends ActionSupport
{
private UserService service;
public UserService getService()
{
return service;
}
public void setService(UserService service)
{
this.service = service;
}
public InputStream getDownloadFile()
{
return this.service.getInputStream();
}
@Override
public String execute() throws Exception
{
return SUCCESS;
}
}
在structs.xml中添加action的声明
<action name="generateExcel" class="generateExcelAction">
<result name="success" type="stream"> <!-- 注意类型为stream -->
<param name="contentType">application/vnd.ms-excel</param> <!-- 文件类型 -->
<param name="contentDisposition">filename="AllUsers.xls"</param> <!-- 浏览器呈现的文件名 -->
<param name="inputName">downloadFile</param> <!-- 文件下载的方法为getDownloadFile -->
</result>
</action>
在applicationContext.xml中添加action的声明
<bean id="generateExcelAction" class="com.test.action.user.GenerateExcelAction" scope="singleton"> <!-- 类没有状态,所以可以配置成singleton -->
<property name="service" ref="userService"></property>
</bean>
在list.jsp中添加链接
<s:a href="index.jsp">Homepage</s:a><br><br>
<s:a href="generateExcel.action">生成excel</s:a>
好了这个功能已经完成了。
3、但是出现了一个问题:程序生成了一个中间文件,如果有一个连接在写这个文件,刚好又有另外一个连接在下载这个文件,这样就会产生冲突。解决方案有两个:1)、对于每一个连接都生成一个名字随机的中间文件,当连接结束及时删除这个中间文件。2)、直接把流输出到内存里,直接生成Inputstream传送客户端,这样就不会生成临时文件了,这种方法在第4点中介绍。
对于第一种方法,又有两种方式:自己写代码随机生成字符串或者借助第三方的库。自己写代码的话如下:
在项目中新建包com.test.util,在该包中新建CharacterUtils.java
package com.test.util; //一般项目中都有util的包,这个包起辅助作用,方法一般是static,这样直接调用就行
import java.util.Random;
public class CharacterUtils
{
public static String getRandomString(int length) //生产长度为length的随机随机字符串,方法一
{
String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < length; ++i)
{
int number = random.nextInt(62);
sb.append(str.charAt(number));
}
return sb.toString();
}
// public static String getRandomString2(int length) //生产长度为length的随机随机字符串,方法二
// {
// Random random = new Random();
// StringBuffer sb = new StringBuffer();
//
// for (int i = 0; i < length; ++i)
// {
// int number = random.nextInt(3);
// long result = 0;
//
// switch (number)
// {
// case 0:
// result = Math.round(Math.random() * 25 + 65);
// sb.append(String.valueOf((char)result));
// break;
// case 1:
// result = Math.round(Math.random() * 25 + 97);
// sb.append(String.valueOf((char)result));
// break;
// case 2:
// sb.append(String.valueOf(new Random().nextInt(10)));
// break;
// }
// }
//
// return sb.toString();
// }
// public static void main(String[] args)
// {
// System.out.println(getRandomString2(10));
// }
}
将UserServiceImpl.java中test.xml生成的地方改为
String fileName = CharacterUtils.getRandomString(10);
fileName = new StringBuffer(fileName).append(".xls").toString();
File file = new File(fileName);
要达到生成随机字符串的效果,也可用apache提供的包,实施如下:
将UserServiceImpl.java中test.xml生成的地方改为
String fileName = RandomStringUtils.randomAlphanumeric(10); //apache 提供的方法
fileName = new StringBuffer(fileName).append(".xls").toString();
File file = new File(fileName);
下面删除这个临时文件,我们采用的方法是在文件生成好之后20分钟线程删除这个文件,并且每次服务器启动的时候在init()方法里检查apache的bin目录下是否有临时文件,如果有的话就删除掉。
在UserServiceImpl.java中加入
new Thread(new Runnable()
{
public void run()
{
try
{
Thread.sleep(20000 * 60); //睡眠20分钟
}
catch (InterruptedException e)
{
e.printStackTrace();
}
file.delete();//删除临时文件
}
}).start();
新建包com.test.servlet,在包中新建DeleteFileServlet.java,这个servlet用来在tomcat启动的时候删除apache的bin目录下的xls临时文件,所以用户不访问这个servlet,这个servlet只需要实现init()方法,而不需要doGet(),doPost(),也不需要servlet-mapping。
package com.test.servlet;
import java.io.File;
import java.io.FileFilter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
public class DeleteFilesServlet extends HttpServlet
{
public void destroy()
{
}
public void init() throws ServletException
{
// 方法一比较简单
// File file = new File("."); //获取当前目录文件
//
// File[] subFiles = file.listFiles();
//
// for(File f : subFiles)
// {
// if(f.getName().endsWith("xls"))
// {
// f.delete();
// }
// }
// 方法二,使用FileFilter
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();
}
}
}
在web.xml中加入
<servlet>
<servlet-name>DeleteFilesServlet</servlet-name>
<servlet-class>com.test.servlet.DeleteFilesServlet</servlet-class>
<load-on-startup>8</load-on-startup>
</servlet>
4、好了现在来看第二种方式,不生成临时文件,直接使用流来输出。这样的话第3步全部不用做,直接在UserServiceImpl.java中
ByteArrayOutputStream os = new ByteArrayOutputStream(); //不生成临时文件
try
{
wb.write(os);
}
catch (IOException e)
{
e.printStackTrace();
}
byte[] content = os.toByteArray();
InputStream is = new ByteArrayInputStream(content);
return is;
到此为止,系统已经全部完成。
附录:
整个笔记参照了[浪曦原创]Struts2.Hibernate3.2.Spring2.0整合(风中叶),这是一个很好的视频教程,适合快速了解SSH
项目源码http://www.kuaipan.cn/file/id_3287587011724175.html