SpringBoot上传解析并保存Excel数据

使用Java完成Excel文件的上传、内容的解析和以及保存操作。重点主要在于使用org.apache.poi包下的Workbook类完成对Excel内容的解析

首先pom文件引入Apache poi,

<!-- https://mvnrepository.com/artifact/org.apache.poi/poi -->
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>3.9</version>
</dependency>

Apache POI提供API给Java程序对Microsoft Office(Excel、Word、PowerPoint等)格式档案读和写的功能

代码顺序为
controller → service → mapper

controller

接口用于接收前台页面上传的excel文件,并对MultipartFile参数做一些基本的判断

@Controller
@RequestMapping("/upload")
public class UploadExcelFileController {

    private static final Logger LOGGER = LoggerFactory.getLogger(UploadExcelFileController.class);

    @Autowired
    private UploadExcelFileService uploadExcelFileService;

    @PostMapping("/excelFile")
    @ResponseBody
    public ResponseResult uploadExcel(@RequestParam(value = "excelFile") MultipartFile file) {
        LOGGER.debug("开始上传Excel文件");
        ResponseResult result = new ResponseResult();
        try {
            if (file == null) {
                // 文件不能为空
                LOGGER.info("file参数为空!");
                result.setCode(IStatusMessage.SystemStatus.NOT_ALLOWED_EMPTY_FILE.getCode());
                result.setMessage(IStatusMessage.SystemStatus.NOT_ALLOWED_EMPTY_FILE.getMessage());
                return result;
            }

            String fileName = file.getOriginalFilename();
            if (!fileName.matches("^.+\\.(?i)(xls)$") && !fileName.matches("^.+\\.(?i)(xlsx)$")) {
                // 文件格式不正确
                LOGGER.info("Excel文件格式不正确!");
                result.setCode(IStatusMessage.SystemStatus.FILE_FORMAT_IS_INCORRECT.getCode());
                result.setMessage(IStatusMessage.SystemStatus.FILE_FORMAT_IS_INCORRECT.getMessage());
                return result;
            }

            long size = file.getSize();
            if (StringUtils.isEmpty(fileName) || size == 0) {
                // 文件不能为空
                LOGGER.info("Excel文件内容为空!");
                result.setCode(IStatusMessage.SystemStatus.NOT_ALLOWED_EMPTY_FILE.getCode());
                result.setMessage(IStatusMessage.SystemStatus.NOT_ALLOWED_EMPTY_FILE.getMessage());
                return result;
            }

            result = uploadExcelFileService.uploadExcel(fileName, file, existUser.getId(), result);

            if (result.getMessage().equals("success")) {
                //保存成功
                LOGGER.info("上传Excel文件,文件上传成功!");
                result.setCode(IStatusMessage.SystemStatus.UPLOAD_EXCEL_SUCCESS.getCode());
                result.setMessage(IStatusMessage.SystemStatus.UPLOAD_EXCEL_SUCCESS.getMessage());
            }
        } catch (ServiceException e) {
            e.printStackTrace();
            result.setCode(IStatusMessage.SystemStatus.REQUEST_FAILED.getCode());
            result.setMessage(IStatusMessage.SystemStatus.REQUEST_FAILED.getMessage());
            LOGGER.error("上传Excel文件异常", e);
        }

        return result;
    }

service

定义service接口


public interface UploadExcelFileService{

    ResponseResult uploadExcel(MultipartFile file,ResponseResult result) throws ServiceException;

}

serviceImpl

实现service接口,主要数据的操作全部在serviceImpl类中实现


@Service
public class UploadExcelFileServiceImpl implements UploadExcelFileService {

    private static final Logger LOGGER = LoggerFactory.getLogger(UploadExcelFileServiceImpl.class);

    @Autowired
    private UploadEexcelMapper uploadEexcelMapper;

    @Override
    public ResponseResult uploadExcel(MultipartFile file, ResponseResult result) throws ServiceException {
        try {
            LOGGER.debug("开始处理Excel文件!");
            InputStream inputStream = file.getInputStream();
            Workbook wb;
            if (fileName.matches("^.+\\.(?i)(xlsx)$")) {
                wb = new XSSFWorkbook(inputStream);
            } else {
                wb = new HSSFWorkbook(inputStream);
            }
            Sheet sheet = wb.getSheetAt(0);
            if (sheet == null) {
                result.setMessage("Excel数据为空!");
                return result;
            }

            // 列数
            int column = sheet.getRow(0).getPhysicalNumberOfCells();
            LOGGER.debug("Excel列数:" + column);
            // 此处可判断Excel列数是否符合要求

            // 行数
            int rows = sheet.getLastRowNum();
            LOGGER.debug("Excel行数:" + rows);
            // 此处可判断Excel行数是否符合要求

            List<ExcelDataBean> excelData = new ArrayList<>();
            ExcelDataBean temporary;

            // 循环Excel
            for (int i = 1; i <= sheet.getLastRowNum(); i++) {
                Row row = sheet.getRow(i);
                if (row == null) {
                    continue;
                }

                temporary = new ExcelDataBean();

                // 用户名
                if (row.getCell(0) != null) {
                    row.getCell(0).setCellType(Cell.CELL_TYPE_STRING);
                    String userName = row.getCell(0).getStringCellValue();
                    if (userName == null || userName.isEmpty()) {
                        result.setMessage("Excel中用户名称为必填项,不能为空,请填写后再进行上传!");
                        return result;
                    }
                    temporary.setUserName(userName);
                }

                // 手机号
                if (row.getCell(1) != null) {
                    row.getCell(1).setCellType(Cell.CELL_TYPE_STRING);
                    String mobile = row.getCell(1).getStringCellValue();
                    if (mobile == null || mobile.isEmpty()) {
                        result.setMessage("Excel中用户手机号为必填项,不能为空,请填写后再进行上传!");
                        return result;
                    }
                    temporary.setMobile(mobile);
                }

                // QQ
                if (row.getCell(2) != null) {
                    row.getCell(2).setCellType(Cell.CELL_TYPE_STRING);
                    String qq = row.getCell(2).getStringCellValue();
                    if (qq == null || qq.isEmpty()) {
                        result.setMessage("Excel中用户QQ为必填项,不能为空,请填写后再进行上传!");
                        return result;
                    }
                    temporary.setQq(qq);
                }
                //添加进list
                excelData.add(temporary);
            }

            // 此处省略其他操作处理
            // 此处省略其他操作处理

            // 做插入处理
            if (excelData.size() > 0) {
                // 将Excel数据插入数据库
                int i = uploadEexcelMapper.insertExcelData(excelData);
                if (i == excelData.size()) {
                    // 数据全部插入成功
                    result.setMessage("success");
                }
            }
            return result;
        } catch (IOException e) {
            e.printStackTrace();
            LOGGER.error(">>>>>>>>>>>>>保存Excel数据到数据库【UploadExcelFileServiceImpl.uploadExcel()】执行异常:" + e);
            throw new ServiceException(e.getMessage());
        }
        result.setMessage("数据保存失败,请稍候重试!");
        return result;
    }

mapper接口

@Mapper
public interface UploadEexcelMapper{

    int insertExcelData(List<ExcelDataBean> item);

}

mapper.xml,使用foreach将数据保存至数据库

    <!-- 将excel数据批量插入数据库 -->
    <insert id="insertExcelData" parameterType="java.util.List">
        INSERT INTO user_from_excel (username,mobile,qq)
        VALUES
        <foreach collection="list" item="item" index="index" separator=",">
            (
            #{item.username},
            #{item.mobile},
            #{item.qq}
            )
        </foreach>
    </insert>

html页面主要是一个input选择框,和一个确认上传按钮

<input id="fileSelect" type="file" name="excelFile"
           accept=".xls,.xlsx" style="margin-top: 6px"/>

<a class="layui-btn" id="uploadExcel" onclick="uploadExcel()">开始上传</a>

js,使用ajax请求调用接口上传excel文件到后台

/**
 * 上传Excel文件
 */
function uploadExcel() {

    //判断是否已经选择了文件
    var selectFile = $("#fileSelect").val();
    if (selectFile == null || selectFile == '') {
        layer.alert("请先选择文件!");
        return;
    }

    layer.confirm('上传前请先确认文件内容是否填写完整,确认上传此文件吗?', {
        icon: 3, move: false, title: '提示', closeBtn: 0,
        btn: ['确认', '返回']
    }, function () {
        let loading = layer.load(2);
        //多次点击只执行一次
        var oneClick = 1;
        if (oneClick == 1) {
            //执行上传文件操作
            $.ajax({
                type: "POST",
                data: new FormData($('#uploadForm')[0]),
                async: false,
                processData: false,
                contentType: false,
                url: rootUrl + "/upload/excelFile",
                beforeSend: function () {
                    oneClick++;
                },
                success: function (datas) {
                    if (datas.code == null || typeof(datas.code) === 'undefined') {
                        layer.alert("非法参数!", {
                            time: 0,
                            closeBtn: 0,
                            btn: ["确定"],
                            yes: function () {
                                layer.closeAll();
                                // 去列表页面
                                window.location.href = "/";
                            }
                        });
                    } else {
                        if (datas.code == 1104) {
                            //文件上传成功
                            layer.alert(datas.message, {
                                time: 0,
                                closeBtn: 0,
                                btn: ["确定"],
                                yes: function () {
                                    layer.closeAll();
                                    // 去列表页面
                                    window.location.href = "/";
                                }
                            });
                        } else {
                            //上传失败
                            layer.alert(datas.message, {
                                time: 0,
                                closeBtn: 0,
                                btn: ["确定"],
                                yes: function (index) {
                                    //只关闭提醒弹窗
                                    layer.close(index);
                                }
                            });
                        }
                    }
                },
                error: function () {
                    layer.alert("操作请求错误,请您稍后再试", function () {
                        layer.closeAll();
                    });
                },
                complete: function () {
                    // 请求完成后
                    oneClick = 1;
                    layer.close(loading);
                }
            });
        }

    });

}

大概整个流程就这样……



其它使用到的类

实体类ExcelDataBean 👇 只简写了用户名、手机号和QQ三个参数

public class ExcelDataBean {

    // 用户名
    private String userName;

    // 手机号
    private String mobile;

    // QQ号
    private String qq;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getMobile() {
        return mobile;
    }

    public void setMobile(String mobile) {
        this.mobile = mobile;
    }

    public String getQq() {
        return qq;
    }

    public void setQq(String qq) {
        this.qq = qq;
    }

    @Override
    public String toString() {
        return "ExcelDataBean{" +
                "userName='" + userName + '\'' +
                ", mobile='" + mobile + '\'' +
                ", qq='" + qq + '\'' +
                '}';
    }
}

请求响应结果封装类ResponseResult,有code、message和obj三个参数

public class ResponseResult implements Serializable {
    
    private static final long serialVersionUID = 728506566686199394L;

    private String code;
    private String message;
    private Object obj;
    
    public ResponseResult() {
        // 默认是返回成功的
        this.code = IStatusMessage.SystemStatus.SUCCESS.getCode();
        this.message = IStatusMessage.SystemStatus.SUCCESS.getMessage();
    }
    
    public ResponseResult(IStatusMessage statusMessage){
        this.code = statusMessage.getCode();
        this.message = statusMessage.getMessage();
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public Object getObj() {
        return obj;
    }

    public void setObj(Object obj) {
        this.obj = obj;
    }

    @Override public String toString() {
        return "ResponseResult{" + "code='" + code + '\'' + ", message='"
                + message + '\'' + ", obj=" + obj + '}';
    }
}

自定义服务层接口异常ServiceException

public class ServiceException extends Exception {

    private static final long serialVersionUID = -8265701868248066795L;

    public ServiceException() {
        super();
    }

    public ServiceException(String message) {
        super(message);
    }

    public ServiceException(Throwable throwable) {
        super(throwable);
    }

    public ServiceException(String message, Throwable throwable) {
        super(message, throwable);
    }

}

响应状态信息类IStatusMessage

public interface IStatusMessage {

    String getCode();

    String getMessage();

    public enum SystemStatus implements IStatusMessage {
        NOT_ALLOWED_EMPTY_FILE("1112", "上传文件不能为空"),
        FILE_FORMAT_IS_INCORRECT("1103", "上传文件格式不正确"),
        NOT_ALLOWED_EMPTY_FILE("1112", "上传文件不能为空"),
        UPLOAD_EXCEL_SUCCESS("1104", "文件上传成功"),
        REQUEST_FAILED("1101", "请求失败,请您稍后再试"),
        ;

        private String code;
        private String message;

        private SystemStatus(String code, String message) {
            this.code = code;
            this.message = message;
        }

        public String getCode() {
            return this.code;
        }

        public String getMessage() {
            return this.message;
        }
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342