从前台导入/导出数据到Excel实现(HSSF)

从前台导入/导出数据库数据实现(HSSF)

一、导出

用户在浏览器主页面,通过选中从数据库中取出的记录,点击"导出",把所有符合条件的数据生成一个excel文件,输出到浏览器;激活文件下载的对话框,用户选择要保存目录,完成导出数据的功能。

  • 使用java生成excel文件:这里处理办公文档的插件使用的是apache-poi

1. 依赖

<!--poi-->
<dependency>
	<groupId>org.apache.poi</groupId>
	<artifactId>poi</artifactId>
	<version>3.15</version>
</dependency>

2. 关键点

  • excel文件中所有的元素都封装成java类,程序员通过java类以及类的对象,达到操作excel文件的目的.
    关键类:
    • HSSFWorkbook:excel文件
    • HSSFSheet:表
    • HSSFRow:行
    • HSSFCell:单元格
  • 所有文件下载的请求只能发同步请求

3. POI的使用

  • 创建一个excel文件
    HSSFWorkbook workbook = new HSSFWorkbook();
    
  • 通过文件,创建一页,可以在创建时给页起名字
    HSSFSheet sheet = workbook.createSheet(); 
    HSSFSheet sheet = workbook.createSheet("页的名字"); 
    
  • 通过页,创建一行
    • 行的创建要通过下标来指定是哪一行
    • 下标是通过从0开始的计数
    • 如下案例所示createRow(0),表示创建的是第一行
    HSSFRow row = sheet.createRow(0); 
    
  • 通过行,来创建行中的单元格
    • 单元格也是要通过下标来指定是哪一格
    • 下标是通过从0开始的计数
    • 如下案例所示createCell(0),表示是在当前行中创建的第一个单元格
    HSSFCell cell = row.createCell(0); 
    
  • 设置单元格的值
    cell.setCellValue("id"); 
    
  • 将文件保存在本地
    OutputStream out = new FileOutputStream(new File("D:\\study\\student.xls"));
    workbook.write(out);
    

4. 导出功能的实现

(1) 前端导出按钮绑定点击事件

$("#exportActivityAllBtn").click(function () {
    if(confirm("确定要导出所有数据吗?")){
        //必须是传统请求
        window.location.href = "workbench/activity/exportActivityAll.do";
    }
})

(2) 后台生成excel文件并传到前台

要点:

  1. 生成excel文件,并填充数据
  2. 为客户浏览器提供下载框,并设置默认的文件名(可以有空格,冒号自动转为下划线)
    response.setContentType("octets/stream");
    response.setHeader("Content-Disposition","attachment;filename=Activity-"+dateStr+".xls");
    
  3. 获取响应流,将数据发送到前台
    out = response.getOutputStream();
    workbook.write(out);
    
  4. Tomcat会自动关闭响应流
@RequestMapping("/exportActivityAll.do")
public void exportActivityAll(HttpServletResponse response){

    //通过业务层查询所有的记录,Activity是个实体类
    List<Activity> aList = activityService.getActivityList();
	//创建一个excel文件 
    HSSFWorkbook workbook = new HSSFWorkbook();
	//通过文件,创建一页
    HSSFSheet sheet = workbook.createSheet();
	//通过页,创建一行
    HSSFRow row = sheet.createRow(0);

    //创建第一行下的第1个单元格,第一行为表头
    HSSFCell cell = row.createCell(0);
    cell.setCellValue("id");

    //创建第一行下的第2个单元格
    cell = row.createCell(1);
    cell.setCellValue("owner");

    //遍历sList取值填充excel
    for(int i=0;i<aList.size();i++){

        //取出每一个市场活动,将数据填充到每一个单元格
        Activity a = aList.get(i);

        row = sheet.createRow(i+1);

        //创建第一行下的第1个单元格
        cell = row.createCell(0);
        cell.setCellValue(a.getId());

        //创建第一行下的第2个单元格
        cell = row.createCell(1);
        cell.setCellValue(a.getOwner());
    }

    //获取当前时间的字符串,格式如下,可以有空格
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    Date date = new Date();
    String dateStr = sdf.format(date);

    //为客户浏览器提供下载框,并设置默认的文件名,冒号自动转为下划线,
    response.setContentType("octets/stream");
    response.setHeader("Content-Disposition","attachment;filename=Activity-"+dateStr+".xls");
    /*
        通过response得到的响应流,如果我们自己没有关闭掉
        tomcat服务器会自动帮我们关闭掉
     */

    OutputStream out = null;
    try {
        //获取响应流
        out = response.getOutputStream();
        workbook.write(out);
        workbook.close();

    } catch (Exception e) {
        e.printStackTrace();
    }
}

二、导入

用户在主页面,点击"导入"按钮,弹出导入数据的模态窗口(BootStrap实现);用户在导入数据的模态窗口,选择要导入的文件,点击"导入"按钮,完成导入市场活动的功能.

  • 处理文件上传的插件:这里使用的是springMVCfile.transferTo(new File(fileName)));
  • 处理办公文档的插件:同样是apache-poi,将excel解析成为java对象

1. 依赖

<!--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>

2. 操作要求

(1)仅支持.xls.xlsx格式的文件
(2)文件大小不超过5MB

3. 配置SpringMVC

因为使用的是SpringMVC框架,所以要在SpringMVC的配置文件中加入以下配置:(注意id必须是multipartResolver

<!-- 配置文件上传解析器 id:必须是multipartResolver-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
	<!--设置上传文件的大小-->
    <property name="maxUploadSize" value="#{1024*1024*5}"/>
    <!--设置上传文件的编码-->
    <property name="defaultEncoding" value="utf-8"/>
</bean>

4. 实际目录和虚拟目录

  • 实际目录:以盘符开始(相当于操作系统的绝对路径)
  • 虚拟目录:以/开始,/代表应用部署后的根目录(相当于java项目的绝对路径)

5. 导入功能实现

(1) 前端实现

要点:

  1. 获取用户上传的文件的绝对地址(value),从绝对地址中截取出后缀名(.后的字符串)(substr),从而判断是否为excel文件
  2. 通过files属性取得文件,判断文件大小(与在配置中判断文件大小选一个即可)
  3. FormData类型的作用是可以提交文本数据,还可以提交二进制数据,通过FormData模拟键值对向服务器提交数据
  4. 文件上传必须用post请求
  5. 发送ajax请求,processDatacontentType参数均为false
//为导入按钮绑定事件,执行导入操作(通过excel文件生成表中的记录)
$("#importActivityBtn").click(function () {
	//获取用户上传的文件的绝对地址
	var activityFileName = $("#activityFile").val();
	//从绝对地址中截取出后缀名(.后的字符串)
	var suffix = activityFileName.substr(activityFileName.lastIndexOf(".")+1);
	//判断是否为excel文件
	if(suffix!="xls" && suffix!="xlsx"){
		alert("不是有效的excel文件");
		return false;
	}

	//取得文件
	/*
		jquery没有为我们提供取得文件的方法,所以我们还是必须要使用到原生js的dom来取
		使用files属性
	 */
	var activityFile = $("#activityFile")[0].files[0];

	//判断文件大小(这种方法和在SpringMVC配置中设置的方法选其一即可)
	if(activityFile.size>1024*1024*5){
		alert("文件大小不超过5MB!");
		return false;
	}

	//FormData是ajax定义的接口,可以模拟键值对向服务器提交数据
	//FormData类型的作用是可以提交文本数据,还可以提交二进制数据.
	var formData=new FormData();
	formData.append("myFile",$("#activityFile")[0].files[0]);

	/*
		contentType:false
			默认情况下,ajax向服务器发送数据之前,
			把所有数据统一按照applciation/x-www-form-urlencoded编码格式进行编码;
			把contentType设置为false,能够阻止这种行为.
		processData:false
			主要是配合contentType使用的,
			默认情况下,ajax把所有数据进行applciation/x-www-form-urlencoded编码之前,
			会把所有数据统一转化为字符串;把proccessData设置为false,可以阻止这种行为.
	*/
	//做文件上传,请求方式必须是post请求
	$.ajax({
		url : "workbench/activity/importActivity.do",
		data : formData,
		type : "post",
		dataType : "json",
		processData : false,
		contentType : false,
		success : function (data) {
			if(data.success){
				//上传成功
				alert("文件上传成功");
			}else{
				alert("文件上传失败");
			}
		}
	})
})

(2) 后台实现

要点:

  1. 通过MultipartFile类,来接收前台传过来的数据
  2. 给文件生成一个名字,不能有特殊字符,这里使用了时间(与导出中的时间不同)
  3. 在做文件上传相关操作的时候,一定要使用的是虚拟路径
  4. 创建一个存放临时文件的文件夹tmpDic,将上传的文件先放到这儿。为了防止idea不随着项目发布该文件夹,要在文件夹中建一个jsp文件(任意一个需要编译的文件)
  5. 通过全局域对象的realPath方法来取得虚拟路径下的真实路径request.getServletContext().getRealPath("/tmpDic");
  6. 将上传回来的二进制解析成excel文件,并将excel保存在临时文件夹中file.transferTo(new File(path+"/"+fileName));
  7. 获取文件的输入流,通过输入流,获取execl文件
    InputStream input = new FileInputStream(path+"/"+fileName);
    //通过输入流,获取execl文件
    HSSFWorkbook workbook = new HSSFWorkbook(input);
    
  8. 将excel文件中的数据循环遍历,提取出来保存到实体类的集合中,再传入dao层保存到数据库中
  9. *注意sheet.getLastRowNum() 返回的最后一行的行数是从0开始算的,即只有一列的话返回0;row.getLastCellNum()返回的最后一个单元格列数是从1开始算的,即只有一个单元格的话返回1
  10. 关闭文件
    具体代码:
@RequestMapping("/importActivity.do")
@ResponseBody
public Map<String,Object> importActivity(@RequestParam("myFile")MultipartFile file, HttpServletRequest request) throws AjaxRequestException {

    //取得文件的名称(与导出不同,文件中不能有空格和冒号等)
    SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
    Date date = new Date();
    String fileName = sdf.format(date);

    //取得文件上传的路径
    /*
        真实路径:盘符下的路径 例如 C: D:
        虚拟路径:找的是当前项目下的路径

        由于文件上传的位置,通过盘符写死的形式,有可能出错(盘符有可能是不存在的)
        所以我们在做文件上传相关操作的时候,一定要使用的是虚拟路径

        我们在webapp的路径下,新建了一个tmpDic的文件夹

        但是如果这个文件夹里面所保存的都是不需要进行编译的文件,
        那么这个文件夹在idea默认的处理下,是不会跟着项目一起发布到服务器中的
        所以我们现在需要想办法,将tmpDic文件夹变成能够进行发布的文件夹
        我们可以在tmpDic下创建一个jsp文件,从此这个文件夹就成为了必须要编译jsp的文件夹
     */

    //我们通过全局域对象realPath方法来取得虚拟路径下的真实路径
    String path = request.getServletContext().getRealPath("/tmpDic");

    try {
        //将上传回来的二进制解析成excel文件,并将excel保存在临时文件夹中
        file.transferTo(new File(path+"/"+fileName));
        //获取文件的输入流
        InputStream input = new FileInputStream(path+"/"+fileName);
        //通过输入流,获取execl文件
        HSSFWorkbook workbook = new HSSFWorkbook(input);
        //取得下标为0(第一页)中的信息
        HSSFSheet sheet = workbook.getSheetAt(0);
        
        List<Activity> aList = new ArrayList<>();
        HSSFRow row = null;

        //遍历行,这里getLastRowNum()方法需要加1
        for(int i=1;i<sheet.getLastRowNum()+1;i++){
            //获取行
            row = sheet.getRow(i);
            
            HSSFCell cell = null;

            //遍历单元格,这里getLastCellNum()方法不需要加1
            for(int j=0;j<row.getLastCellNum();j++){
                //获取单元格
                cell = row.getCell(j);

                if(j==0){
                    a.setOwner(cell.getStringCellValue());
                }else if(j==1){
                    a.setName(cell.getStringCellValue());
                }
            }
            aList.add(a);
        }
        //调用业务层,业务层调用dao层,保存数据
        activityService.saveActivityList(aList);
        //关闭文件
        workbook.close();

    } catch (Exception e) {
        e.printStackTrace();
        throw new AjaxRequestException();
    }
    Map<String , Object> map = new HashMap<>();
    map.put("success", true);
    return map
}
发布了45 篇原创文章 · 获赞 46 · 访问量 1841

猜你喜欢

转载自blog.csdn.net/zyx1260168395/article/details/103237952