一、先在pom.xml中引入commons-fileupload依赖
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
二、JSP页面
给导出按钮设置id=”importActivityBtn”,添加单击事件,发送异步请求
//文件上传三个条件:<input type="file"> 。要post请求,因为post能提交文件,而get不行。表单编码格式multipart/form-data
$("#importActivityBtn").click(function () {
// $("#activityFile").val();只能获取文件名
var activityFileName = $("#activityFile").val();
//截取activityFileName中最后一个.索引再加一 后面的字符串
var suffix=activityFileName.substr(activityFileName.lastIndexOf(".")+1).toLocaleLowerCase();//转为小写 xls,Xls,XLS,xLs...都是excel文件
if(suffix!="xls"){
alert("导入活动仅支持xls文件");
return;
}
//获取文件 .get(0)获得dom对象,dom对象里才有files
var activityFile = $("#activityFile").get(0).files[0];//可以得到文件数组,但是浏览器只能选择一个,files[0]拿数组的第一个
//判断文件大小
if(activityFile.size>5*1024*1024){//activityFile.size()单位是字节
alert("文件大小不能超过5MB");
return;
}
//FormData是ajax提供的接口,可以模拟键值对(: =)向后台提供参数,不仅能提交文本数据,还能提交二进制数据(文件);
var formData = new FormData();
formData.append("activityFile",activityFile);//前面名字要和Controller中参数名字一致
//上传文件需要加这两个属性processData:false;contentType:false;
$.ajax({
url:'importActivity.do',
data:formData,
type:"post",
processData:false,//设置ajax向后台提交参数之前,是否把参数转换为字符串:true---是,false--不是,默认是true
contentType:false,//设置ajax向后台提交参数之前,是否把所有的参数统一按urlencoded编码(只能对字符串进行编码) true---是,false---不是
dataType:"json",
success:function (data) {
if(data.code=="1"){
alert("成功导入"+data.retData+"条活动");
$("#importActivityModal").modal("hide");//关闭文件上传的窗口
queryActivityByConditionForPage(1,$("#demo_page1").bs_pagination('getOption','rowsPerPage'));
}else {
alert(data.message);
$("#importActivityModal").modal("show");//本来就是开着的,不关闭文件上传的窗口
}
}
});
});
三、在springmvc配置文件中配置文件上传解析器
<!-- 配置文件上传解析器 id:必须是multipartResolver&ndash-->
<!--CommonsMultipartResolver的对象从post请求体中拿数据,文件 id名不能改-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="#{1024*1024*5}"/><!--文件大小,其实在前台就验证大小了,不用这个-->
<property name="defaultEncoding" value="utf-8"/>
</bean>
四、Controller层,读取文件每一行每一列的值,封装成实体类对象,插入数据库
// 配置springmvc的文件上传解析器,CommonsMultipartResolver的对象从post请求体中拿数据,文件。把文件给 MultiparFile activityFile
@ResponseBody
@RequestMapping("/importActivity.do")
public Object importActivity(MultipartFile activityFile,HttpSession session){
User user = (User) session.getAttribute(Contants.SESSION_USER);//用户登录时就用户信息放到session中,现在拿出来
ReturnObject returnObject = new ReturnObject();//返回的对象
try {
String originalFilename = activityFile.getOriginalFilename();//获取原来的名字,也就是用户上传时文件的名字
InputStream inputStream = activityFile.getInputStream();
//根据excel文件生成HSSFWorkbook对象,封装了excel文件的所有信息
HSSFWorkbook wb = new HSSFWorkbook(inputStream);
//根据wb获取HSSFSheet对象,封装了一页的所有信息
HSSFSheet sheet = wb.getSheetAt(0);//只有一页
//根据sheet获取HSSFRow对象,封装了一行的所有信息
HSSFRow row = null;
HSSFCell cell = null;
Activity activity=null;
List<Activity> activityList = new ArrayList<>();
//从excel文件中遍历每一行
for(int i=1;i<=sheet.getLastRowNum();i++){//getLastRowNum()最后一行的下标,所以<=
row=sheet.getRow(i);
activity=new Activity();
activity.setId(UUIDUtils.getUUID());//自定义的工具方法,生成32位随机id
activity.setOwner(user.getId());
activity.setCreateTime(DateUtils.formateDateTime(new Date()));//自定义工具方法,把当前事件转为yyyy-MM-dd
activity.setCreateBy(user.getId());
//在一行中遍历每一列
for(int j=0;j<row.getLastCellNum();j++){//getLastCellNum()最后一列的下标+1,所以<
//根据row获取HSSFCell对象,封装了一列的所有信息
cell=row.getCell(j);
String cellValue=HSSFUtils.getCellValueForStr(cell);//自定义工具类,判断获取的列的值是什么类型,再赋值给cellValue
if(j==0){
activity.setName(cellValue);
}else if(j==1){
activity.setStartDate(cellValue);
}else if(j==2){
activity.setEndDate(cellValue);
}else if(j==3){
activity.setCost(cellValue);
}else if(j==4){
activity.setDescription(cellValue);
}
}
//把封装好的每一个activity放到activityList中
activityList.add(activity);
}
//把activityList插入数据库
int ret = activityService.saveCreateActivityByList(activityList);
returnObject.setCode(Contants.RETURN_OBJECT_CODE_SUCCESS);
returnObject.setRetData(ret);
} catch (IOException e) {
e.printStackTrace();
returnObject.setCode(Contants.RETURN_OBJECT_CODE_FAIL);
returnObject.setMessage("系统繁忙,请稍后重试...");
}
return returnObject;
}
记得配置扫描Contrller
五、获取Cell值的工具类
因为获取每一列的值需要先判断值是什么类型才能获取,所以生成工具类方便调用
import org.apache.poi.hssf.usermodel.HSSFCell;
public class HSSFUtils {
/**
* 从指定的HSSFCell对象获取列的值
* @return
*/
public static String getCellValueForStr(HSSFCell cell){
// 因为cell.get__CellValue获取列的值方法必须指定数据类型,所以要判断一下列的数据类型是什么再获取
// 用cell.getCellType()可以得到列的数据类型,是什么数据类型就用什么cell.get__CellValue获取
String ret="";
if(cell.getCellType()==HSSFCell.CELL_TYPE_STRING){//字符串类型
ret=cell.getStringCellValue();
}else if(cell.getCellType()==HSSFCell.CELL_TYPE_NUMERIC){//数值类型 默认Double
ret=Double.toString(cell.getNumericCellValue());
//ret是字符型的,不是继承关系不能强转。它不是一个封装类,不是一个对象,所以不能用toString().。。cell.getNumericCellValue()+""
}else if(cell.getCellType()==HSSFCell.CELL_TYPE_BOOLEAN){//布尔类型
ret=cell.getBooleanCellValue()+"";
}else if(cell.getCellType()==HSSFCell.CELL_TYPE_FORMULA){//公式类型
ret=cell.getCellFormula()+"";
}else if(cell.getCellType()==HSSFCell.CELL_TYPE_BLANK){//空类型
ret="null";
}else if(cell.getCellType()==HSSFCell.CELL_TYPE_ERROR){//错误类型
ret="error";
}
return ret;
}
}
六、Service和实现类
public interface ActivityService {
//批量插入市场活动
int saveCreateActivityByList(List<Activity> activityList);
}
@Service("activityService")
public class ActivityServiceImpl implements ActivityService{
@Autowired
ActivityMapper activityMapper;
@Override
public int saveCreateActivityByList(List<Activity> activityList) {
return activityMapper.insertActivityByList(activityList);
}
}
记得配置扫描Service
七、Dao和mapper
public interface ActivityMapper {
//批量插入市场活动
int insertActivityByList(List<Activity> activityList);
}
<insert id="insertActivityByList" parameterType="com.ycj.domain.Activity">
insert into tbl_activity(id, owner, name, start_date, end_date, cost, description, create_time, create_by)
value
<foreach collection="list" item="obj" separator=",">
<!--注意这里取的是实体类的属性,要用驼峰命名-->
(#{obj.id},#{obj.owner},#{obj.name},#{obj.startDate},#{obj.endDate},#{obj.cost},#{obj.description},#{obj.createTime},#{obj.createBy})
</foreach>
</insert>
记得配置mapper注解扫描器
八、总结
难点一在于Controller层中两个for循环读取每一行每一列的值,而读Cell,也就是列的值需要先判断是什么类型的值,cell.get__CellValue()有6种类型;二是发送异步请求使用FormData接口把文件放请求体中上传。