POI 实现 Excel 文件上传下载及大数据导出处理

Java 中操作 Excel 的有两种比较主流的工具包: JXL 和 POI 。JXL 只能操作 Excel 95、97、2000 等老版本格式数据,也即以 .xls 为后缀的 excel。而 POI 可以操作 Excel 95 及以后的版本,即可操作后缀为 .xls 和 .xlsx 两种格式的 Excel。

POI 全称 Poor Obfuscation Implementation,利用 POI 接口可以通过 Java 操作 Microsoft Office 套件工具的读写功能,POI 支持 Office 的所有版本。

1. Excel 文件上传与下载

本节是对 Excel 文件的下载和上传实现;其在 CRM 系统中比较常见,即需要实现下载和上传表格数据信息,总的来说下载是比较容易实现,上传由于格式必须与数据库字段对应,显得有些麻烦;该 demo 实验为了方便期间,没有与数据库进行交互,使用的是 Servlet 实现;

依赖包如下:

  • poi.jar(Java 操作文件 API 相关)
  • commons-io.jar(文件流相关)
  • commons-fileupload.jar(文件传输相关)

jar 包官网下载地址如下:

代码实现如下:

web.xml 配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
    xmlns="http://java.sun.com/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
  <servlet>
    <servlet-name>PoiServlet</servlet-name>
    <servlet-class>cn.smart4j.controller.PoiServlet</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>PoiServlet</servlet-name>
    <url-pattern>/poiTest.jsp</url-pattern>
  </servlet-mapping>
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
</web-app>

JSP 页面 index.jsp:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>poi-Demo</title>
  </head>
  <body>
    <a href="poiTest.jsp?cmd=downFile">下载</a><br>
    <form action="poiTest.jsp?cmd=uploadFile" method="post" enctype="multipart/form-data">
        <input type="file" name="file">
        <input type="submit" value="submit">
    </form>
  </body>
</html>

controller 层代码实现:

PoiServlet.java 代码如下:

package cn.smart4j.controller;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;

public class PoiServlet extends HttpServlet {    
    private static final long serialVersionUID = 1L;  

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String methodName = request.getParameter("cmd");        
        if("downFile".equals(methodName)){
            downFile(request,response);
        }else if("uploadFile".equals(methodName)){
            uploadFile(request,response);
        }

    }    

    private void uploadFile(HttpServletRequest request,
            HttpServletResponse response) {        
        if(ServletFileUpload.isMultipartContent(request)){
            DiskFileItemFactory factory = new DiskFileItemFactory();
            factory.setSizeThreshold(1024*512);
            factory.setRepository(new File("D:/tempFile"));
            ServletFileUpload fileUpload=new ServletFileUpload(factory);
            fileUpload.setFileSizeMax(10*1024*1024);//设置最大文件大小
            try {                
                @SuppressWarnings("unchecked")
                List<FileItem> items=fileUpload.parseRequest(request);//获取所有表单
                for(FileItem item:items){                    
                    //判断当前的表单控件是否是一个普通控件
                    if(!item.isFormField()){                        
                        //是一个文件控件时
                        String excelFileName = new String(item.getName().getBytes(), "utf-8"); //获取上传文件的名称
                        //上传文件必须为excel类型,根据后缀判断(xls)
                        String excelContentType = excelFileName.substring(excelFileName.lastIndexOf(".")); //获取上传文件的类型                 
                        if(".xls".equals(excelContentType)){
                            POIFSFileSystem fileSystem = new POIFSFileSystem(item.getInputStream());
                            HSSFWorkbook workbook = new HSSFWorkbook(fileSystem);
                            HSSFSheet sheet = workbook.getSheetAt(0);                            
                            int rows = sheet.getPhysicalNumberOfRows();                           
                            for (int i = 0; i < rows; i++) {
                                HSSFRow row = sheet.getRow(i);                               
                                int columns = row.getPhysicalNumberOfCells();                               
                                for (int j = 0; j < columns; j++) {
                                    HSSFCell cell = row.getCell(j);
                                    String value = this.getCellStringValue(cell);
                                    System.out.print(value + "|");
                                }
                            }
                        }else{
                            System.out.println("必须为excel类型");
                        }                        
                        response.sendRedirect("index.jsp");
                    }
                }
            }catch (Exception e) {
                e.printStackTrace();
            }
        }
    }    

    private void downFile(HttpServletRequest request,
            HttpServletResponse response) {
        response.setContentType("application/vnd.ms-excel;charset=UTF-8");
        response.setHeader("Content-Disposition", "attachment;filename=data.xls"); 

        ServletOutputStream stream = null;        
        try {
            stream = response.getOutputStream();
        } catch (IOException e1) {
            e1.printStackTrace();
        }
        HSSFWorkbook workbook = new HSSFWorkbook();
        HSSFCellStyle style = workbook.createCellStyle();
        style.setAlignment(HSSFCellStyle.ALIGN_CENTER);//左右居中样式

        HSSFSheet sheet = workbook.createSheet("sheetName");
        sheet.setColumnWidth(0, 2000);
        sheet.setColumnWidth(1, 5000);        
        //创建表头(第一行)
        HSSFRow row = sheet.createRow(0);        
        //列
        HSSFCell cell = row.createCell(0);
        cell.setCellValue("姓名");
        cell.setCellStyle(style);
        HSSFCell cell2 = row.createCell(1);
        cell2.setCellValue("年龄");
        cell2.setCellStyle(style);        
        //创建数据行
        for(int i =1;i<=20;i++) {
            HSSFRow newRow = sheet.createRow(i);
            newRow.createCell(0).setCellValue("smart"+i);
            newRow.createCell(1).setCellValue(i);
        }        try {
            workbook.write(stream);
            stream.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(stream != null) {
                stream.close();
            }
        }
    }    

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }    

    //转换单元格数据类型
    public String getCellStringValue(HSSFCell cell) {   
        String cellValue = "";   
        switch (cell.getCellType()) {   
        case HSSFCell.CELL_TYPE_STRING:   
            cellValue = cell.getStringCellValue();   
            if(cellValue.trim().equals("")||cellValue.trim().length()<=0)   
                cellValue=" ";   
            break;   
        case HSSFCell.CELL_TYPE_NUMERIC:   
            cellValue = String.valueOf(cell.getNumericCellValue());   
            break;   
        case HSSFCell.CELL_TYPE_FORMULA:   
            cell.setCellType(HSSFCell.CELL_TYPE_NUMERIC);   
            cellValue = String.valueOf(cell.getNumericCellValue());   
            break;   
        case HSSFCell.CELL_TYPE_BLANK:   
            cellValue=" ";   
            break;   
        case HSSFCell.CELL_TYPE_BOOLEAN:   
            break;   
        case HSSFCell.CELL_TYPE_ERROR:   
            break;   
        default:   
            break;   
        }   
        return cellValue;   
    }
}

2. 大数据导出解决方案

POI 之前的版本不支持大数据量处理,如果数据过多则经常报 OOM 错误,有时候调整 JVM 大小效果也不是太好。3.8 版本的 POI 新出来了 SXSSFWorkbook,可以支持大数据量的操作,只是 SXSSFWorkbook 只支持 .xlsx 格式,不支持 .xls 格式。

3.8 版本的 POI 对 Excel 的导出操作,一般只使用 HSSFWorkbook 以及 SXSSFWorkbook,HSSFWorkbook 用来处理较少的数据量,SXSSFWorkbook 用来处理大数据量以及超大数据量的导出。

代码实现如下所示:

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
public class SXSSFTest {    
    public static void main(String[] args) throws IOException {        
        // 创建基于stream的工作薄对象的
        SXSSFWorkbook wb = new SXSSFWorkbook(100); 
        Sheet sh = wb.createSheet();        
        // 使用createRow将信息写在内存中。
        for (int rownum = 0; rownum < 1000; rownum++) {
            Row row = sh.createRow(rownum);            
            for (int cellnum = 0; cellnum < 10; cellnum++) {
                Cell cell = row.createCell(cellnum);
                String address = new CellReference(cell).formatAsString();
                cell.setCellValue(address);
            }

        }        
        // 当使用getRow方法访问的时候,将内存中的信息刷新到硬盘中去。
        for (int rownum = 0; rownum < 900; rownum++) {
            System.out.println(sh.getRow(rownum));
        }
        for (int rownum = 900; rownum < 1000; rownum++) {
            System.out.println(sh.getRow(rownum));
        }        
        // 写入文件中
        FileOutputStream fos = new FileOutputStream("e://temp.xlsx");
        wb.write(fos);        
        // 关闭文件流对象
        fos.close();
        System.out.println("基于流写入执行完毕!");
    }
}

在此基础上再优化的方案是导出的 Excel 表格生成多个工作表即生成多个 sheet。

代码实现如下:

import java.io.IOException;import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;import java.util.Date;
import java.util.LinkedHashMap;import java.util.List;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import com.common.DateFormatUtil;public class ExlUtil {    
/**
     * @param excelHeader  表头信息
     * @param list  要导出到excel的数据源,List类型
     * @param sheetName  表名
     * @return
     */
    public static ResponseEntity<byte[]> getDataStream(ExcelHeader excelHeader,
            List list, String sheetName) {
        LinkedHashMap<String, List> map = new LinkedHashMap<String, List>();
        List<String[]> headNames = new ArrayList<String[]>();
        List<String[]> fieldNames = new ArrayList<String[]>();
        String[] sheetNames = new String[100];        
        //处理Excel生成多个工作表 
        //定义为每个工作表数据为50000条
        if (list.size() > 50000) {            
            int k = (list.size() + 50000) / 50000;            
            for (int i = 1; i <= k; i++) {                
                if (i < k) {
                    map.put(sheetName + i,
                            list.subList((i - 1) * 50000, i * 50000));
                } else {
                    map.put(sheetName + i,
                            list.subList((i - 1) * 50000, list.size()));
                }

                headNames.add(excelHeader.getHeadNames().get(0));
                fieldNames.add(excelHeader.getFieldNames().get(0));
                sheetNames[i - 1] = sheetName;
            }

        } else {
            map.put(sheetName, list);
            headNames.add(excelHeader.getHeadNames().get(0));
            fieldNames.add(excelHeader.getFieldNames().get(0));
            sheetNames[0] = sheetName;
        } 

        byte[] buffer = null;   

        try {
            buffer = ExcelUtil2.output(headNames, fieldNames, sheetNames, map);

        } catch (IllegalArgumentException | IllegalAccessException
                | IOException e) {
            e.printStackTrace();
        }
        HttpHeaders headers = new HttpHeaders();                           headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);        
        try {
            sheetName = new String(sheetName.getBytes("gbk"), "iso-8859-1");
        } catch (UnsupportedEncodingException e) {            
            e.printStackTrace();
        }
        String fileGenerateTime = DateFormatUtil.toStr(new Date());
        headers.setContentDispositionFormData("attachment", sheetName
                + fileGenerateTime + ".xlsx");        
        return new ResponseEntity<byte[]>(buffer, headers, HttpStatus.CREATED);
    };
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值