实现文件上传的方式用很多种,文件类型也有许多种类,这篇文章主要针对Excel进行解析,这里解析文件使用的是spring框架自带的类进行解析。首先,有几个点需要知道,用户上传的excel文件,前端如何保存该文件并发送给后端,后端控制器如何获取文件并解析,持久层如何保存文件数据,这是我们需要知道的大致流程,这里从持久层写到视图层,有点逆向思维,那我们开始吧!
首先导入相关依赖
pom依赖
//POI提供API给Java程序对Microsoft Office格式档案读和写的功能
<!--poi依赖-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.15</version>
</dependency>
<!-- 接收文件上传依赖 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
从持久层开始分析,对保存数据而言,用户上传的数据肯定不止一行数据,那么可以封装集合进行批量保存,集合有很多种,根据分析进行选择,这里最适合选择list集合来保存数据,这里确定后,控制层也几乎确定了。
mapper层的sql语句,参数类型是list集合中的具体对象的参数类型,这里类型也可以可list,都是正确的,这里建议写具体参数类型,因为文档中是写具体类型,使用动态sql把集合中的对象都遍历出来,赋值给obj,这里的obj只是自己设置的参数名,obj代表集合中的对象,将对象的属性都放到插值表达式中,完成数据的添加。
<insert id="insertImportExcel" parameterType="com.chen.domain.Activity">
insert into tbl_activity(id, owner, name, cost, description )
values
<foreach collection="list" separator="," item="obj">
(#{obj.id},#{obj.owner},#{obj.name},#{obj.cost},#{obj.description})
</foreach>
</insert>
service调用mapper层
@Service
public class ActivityServiceImpl{
//注入mapper层
@Autowired
ActivityMapper activityMapper;
@Override
public int insertImportExcel(List<Activity> list) {
return activityMapper.insertImportExcel(list);
}
}
springMVC注册文件上传解析器
//这里需要注意的是id名称必须为multipartResolver,不然springMVC无法识别
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
//设置字符集
<property name="defaultEncoding" value="utf-8"/>
//设置最大接收大小
<property name="maxUploadSize" value="#{1024*1024*80}"/>
</bean>
接收前端excel文件
@Controller
public class ActivityController {
@Autowired
ActivityService activityService;
/**
* 导入活动列表
* MultipartFile 该对象用于接收前端传来的文件
* 使用该对象接收文件需要在springMvc配置信息中注册文件上传解析器CommonsMultipartResolver
* HttpSession 当前会话
* @return 返回保存成功或失败提示信息
*/
@RequestMapping("/activity/importActivities.do")
@ResponseBody
public Object importActivities(MultipartFile file, HttpSession session) throws Exception {
//创建一个文件对象,并以流的方式读取该文件
HSSFWorkbook wb = new HSSFWorkbook(file.getInputStream());
//当前登录用户
User user = (User) session.getAttribute(Changeful.USER_SESSION_NAME);
//获取文件对象中的页对象,0表示第一页,1表示第二页,依次类推...
HSSFSheet sheetAt = wb.getSheetAt(0);
//在循环外定义空引用
HSSFRow row = null;
HSSFCell cell = null;
Activity activity = null;
List<Activity> activityList = new ArrayList<>();
//将页中的每一行循环出来,从第二行开始获取数据
//第一行是描述整列的信息,不用保存到数据库
//sheetAt.getLastRowNum()获取最后行数,这里注意是小于等于
for (int i = 1; i <= sheetAt.getLastRowNum(); i++) {
//获取行对象
row = sheetAt.getRow(i);
//设置主键值及上传文件者
activity = new Activity();
activity.setId(UUIDUtil.getUUID());//设置主键
activity.setOwner(user.getId());//设置所有者
//将行中的每一列数据循环出来
for (int j = 0; j < row.getLastCellNum(); j++) {
cell = row.getCell(j);
//因为excel每个单元格有数据类型,且与java中数据类型有些不同
//所有封装一个工具类用于判断excel的数据类型
//使用工具类确定列的类型
switch (j) {
case 0:
activity.setName(ExcelCellValueTypeUtil.getCellValue(cell));
case 1:
activity.setStartDate(ExcelCellValueTypeUtil.getCellValue(cell));
case 2:
activity.setEndDate(ExcelCellValueTypeUtil.getCellValue(cell));
case 3:
activity.setCost(ExcelCellValueTypeUtil.getCellValue(cell));
case 4:
activity.setDescription(ExcelCellValueTypeUtil.getCellValue(cell));
}
}
//每列循环完获取到一个活动对象,添加到list集合中
activityList.add(activity);
}
//返回添加条数,返回给前台
int count = activityService.insertImportExcel(activityList);
ReturnJson returnJson = new ReturnJson();
//简单的判断,如果添加条数大于1行,表示上传成功
if (count>0){
returnJson.setCode(Changeful.RETURN_JSON_CODE_SUCCESS_STATUS);
returnJson.setObject(count);
}else {
returnJson.setCode(Changeful.RETURN_JSON_CODE_FAILURE_STATUS);
returnJson.setMessage("系统繁忙,请稍后重试!");
}
return returnJson;
}
}
用于判断excel中表中列的数据类型工具类
//将excel中的数据都转换成字符串类型,这样较容易的保存
public class ExcelCellValueTypeUtil {
public static String getCellValue(HSSFCell cell) {
String value = "";
switch (cell.getCellType()) {
case HSSFCell.CELL_TYPE_NUMERIC:
value = cell.getNumericCellValue() + "";
break;
case HSSFCell.CELL_TYPE_STRING:
value = cell.getStringCellValue();
break;
case HSSFCell.CELL_TYPE_BOOLEAN:
value = cell.getBooleanCellValue() + "";
break;
case HSSFCell.CELL_TYPE_FORMULA:
value = cell.getCellFormula();
break;
default:
value = "";
}
return value;
}
}
后端代码完成之后,来看看前端如何获取文件吧!
//点击导入市场活动按钮
<script type="text/javascript">
$(function () {
$("#importActivityBtn").click(function () {
//截取文件结尾,判断是否为xls文件
var val = $("#activityFile").val().substr($("#activityFile").val().lastIndexOf(".")+1).toLowerCase();
if ("xls"!=val){
alert("只支持.xls格式!");
return;
}
//当用户选择文件后,前端会将该文件保存在dom对象中
//获取该节点的dom对象,再获取files中的第一个文件
//files可以保存多个用户上传文件,相当于一个数组,但是当下大部分浏览器只支持上传一个文件,所有获取该数组中的第一个文件对象
var file = $("#activityFile").get(0).files[0];
//判断文件大小
if(file.size>1024*1024*5){
alert("文件大小不超过5MB!");
return;
}
//这里上传文件和平时携带参数有所不同,需要创建formData 对象才能将文件传送到后台
var formData = new FormData();
formData.append("file",file);
//发送异步请求
$.ajax({
url: "workbench/activity/importActivities.do",
data:formData,
contentType:false,//默认请求下,ajax每次向后台发送请求,都会把所有的参数统一进行urlencoded编码,设置contentType=false,可以阻止这种行为
processData:false,//默认请求下,ajax每次向后台发送请求,都会把多有的参数统一转化成字符串,设置processData=false,可以阻止这种行为
type: 'post',
dataType: "json",
success: function (data) {
if (data.code==1){
//导入成功,提示成功导入记录条数,关闭模态窗口,刷新活动列表,显示第一页数据,保持每页显示条数不变
alert("成功导入"+data.object+"条市场活动");
$("#importActivityModal").modal("hide");
//这里调用的是分页插件
queryActivityMethod(1, $("#turnPage").bs_pagination('getOption', 'rowsPerPage'));
}else{
//导入失败,提示信息,模态窗口不关闭,列表也不刷新
alert(data.message);
$("#importActivityModal").modal("show")
}
}
})
});
}
</script>
//这里注意:上传文件只能类型为type="file",不然前端将无法获取文件对象
<div class="modal-body" style="height: 350px;">
<div style="position: relative;top: 20px; left: 50px;">
请选择要上传的文件:<small style="color: gray;">[仅支持.xls或.xlsx格式]</small>
</div>
<div style="position: relative;top: 40px; left: 50px;">
<input type="file" name="file" id="activityFile">
</div>
<div style="position: relative; width: 400px; height: 320px; left: 45% ; top: -40px;">
<h3 style="color: red"> 重要提示:</h3>
<ul>
<li>操作仅针对Excel,仅支持后缀名为XLS/XLSX的文件。</li>
<li>给定文件的第一行将视为字段名。</li>
<li>请确认您的文件大小不超过5MB。</li>
<li>日期值以文本形式保存,必须符合yyyy-MM-dd格式。</li>
<li>日期时间以文本形式保存,必须符合yyyy-MM-dd HH:mm:ss的格式。</li>
<li>默认情况下,字符编码是UTF-8 (统一码),请确保您导入的文件使用的是正确的字符编码方式。</li>
<li>建议您在导入真实数据之前用测试文件测试文件导入功能。</li>
</ul>
</div>
</div>
至此,从后台到前台的代码已经完成,但还有很多不足的地方,需要根据客户进行完善,根据客户需求进行开发,那么,感谢你的观看,有错误的地方,请帮忙指出,感激不尽!!!