springboot+poi 导入导出 反射

代码 :https://github.com/goodboyQAQ/poi

一.pom文件

    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi</artifactId>
        <version>3.15</version>
    </dependency>
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml-schemas</artifactId>
        <version>3.15</version>
    </dependency>
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml</artifactId>
        <version>3.15</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>

二.创建实体类

Result类保存一些数据信息返回给前端

@Data //生成 getting 和 setting ,equals、canEqual、hashCode、toString 方法
public class Result<T> {
    private String msg;
    private boolean success;
    private T data;

    public Result(){
        this.success=false;
        this.msg="系统错误";
    }
}

Company是我们导入导出的数据实体类

import lombok.Data;
import lombok.EqualsAndHashCode;
import org.wang.poi.annotation.ExcelTitle;

@Data  //生成 getting 和 setting ,equals、canEqual、hashCode、toString 方法
//重写hashcode,equals判断时(id,name,tel相同就true)
@EqualsAndHashCode(callSuper = false, exclude = {"updateTime"})
//导入导出需要用到的字段
@ExcelTitle(value={"id","name","tel"},title={"编号(不能修改)","名称","电话"})
public class Company {
    private String id;
    private String name;
    private String tel;
    private String updateTime;

}

上面使用了一个自定义的注解

//指明修饰的注解,可以被例如javadoc此类的工具文档化
@Documented
 // 注解会在class字节码文件中存在,在运行时可以通过反射获取到 
@Retention(RetentionPolicy.RUNTIME)  
// 可作用在接口、类、枚举、注解
@Target(ElementType.TYPE)   
public @interface ExcelTitle {
    //字段顺序
    String[] value();
    //中文表头顺序
    String[] title();
}

三.controller类
为了方便理解,从顶层开始

@RestController
@Slf4j
public class CompanyController {

    @Autowired
    private FileUtil fileUtil;

    @Autowired
    private PoiUtil poiUtil;

    @Autowired
    private CompanyService companyService;


    //下载导入模板
    @RequestMapping(value="temp",method=RequestMethod.GET)
    public void temp(HttpServletResponse response){
        String fileName="company.xlsx"; //传入下载工具类生成下载文件名
        try{
            //user.xlsx模板文件放在resource/template下
            InputStream is=this.getClass().getResourceAsStream("/templates/company.xlsx");
            //获取输入流后实现下载
            fileUtil.download(is,fileName,response);
        }catch(Exception e){
            log.error(e.getMessage(),e);
        }
    }

    @RequestMapping(value="upload",method=RequestMethod.POST)
    public Result uplaod(@RequestParam("file")MultipartFile file){
        //MultipartFile spring支持的处理表单的file很方便
        Result result=new Result();
        try{
            //通过文件获得工作簿
            Workbook wb=poiUtil.getWorkBook(file);
            //将数据解析为我们的实体类集合
            List<Company> list=poiUtil.importExcel(wb,Company.class); //解析导入的数据
            companyService.importCompany(list);  //存入数据库
            result.setMsg("导入成功");
            result.setSuccess(true);
        }catch(Exception e){
            log.error(e.getMessage(),e);
        }
        return result;
    }

    @RequestMapping(value="download",method=RequestMethod.GET)
    public Result download(Company company,HttpServletResponse response){
        Result result=new Result();
        try{
            //查询数据
            List<Company> list=companyService.exportData(company);
            if(list.size()==0){
                result.setMsg("数据为空");
                return result;
            }
            String fileName="company.xlsx";
            InputStream is=this.getClass().getResourceAsStream("/templates/company.xlsx");
            poiUtil.exportData(fileName,list,response,Company.class);
            result.setSuccess(true);
            result.setMsg("导出成功");
        }catch(Exception e){
            log.error(e.getMessage(),e);
        }
        return result;

    }
}

四.service层
dao数据库查询,就不写了

@Service
public class CompanyServiceImpl  implements CompanyService {
    @Autowired
    private CompanyDao companyDao;

    @Override
    public void importCompany(List<Company> list) {
        if(list.size()==0){
            return;
        }
        List<Company> insertList=new ArrayList<>();
        List<Company> updateList=new ArrayList<>();
        for(Company company:list){
    	    //空值判断
            if(StringUtils.isNotEmpty(company.getId())){
                //通过id去数据库查找是否存在该条数据
                Company c=companyDao.getCompanyById(company.getId());
                //使用lombok的@EqualsAndHashCode重写了hashcode
                if(c!=null && !company.equals(c)){ //id查找的数据存在,且有更改
                    updateList.add(company);
                }
            }else{
                insertList.add(company);
            }
        }
        if(insertList.size()!=0) {
            companyDao.addCompanyList(insertList);
        }
        for(Company company:updateList){
            companyDao.updateCompany(company);
        }

    }

    @Override
    public List<Company> exportData(Company company) {
        return companyDao.exportData(company);
    }
}

五.FileUtil工具类

@Slf4j
@Component
public class FileUtil {

    /**
     *  下载文件
     * @param is  输入流
     * @param fileName  文件名
     * @param response
     */
    public void download(InputStream is, String fileName, HttpServletResponse response){
            response.setContentType("multipart/form-data");
            response.addHeader("Content-Disposition", "attachment;filename=" + fileName);
            byte[] buffer=new byte[1024];
            BufferedInputStream bis=null;
            OutputStream os=null;
            try{
                bis=new BufferedInputStream(is);
                os=response.getOutputStream();
                int i;
                while((i=bis.read(buffer))!=-1){
                    os.write(buffer,0,i);
                }
            }catch(Exception e){
                log.error(e.getMessage(),e);
            }finally {
                try{
                    if(bis!=null){
                        bis.close();
                    }
                }catch (Exception e){
                    log.error("缓冲输入流关闭异常");
                }
                try{
                    if(os!=null){
                        os.close();
                    }
                }catch (Exception e){
                    log.error("输出流流关闭异常");
                }
            }
    }

    public void upload(MultipartFile file){
        if(file.isEmpty()){
            return;
        }
        String fileNmae=file.getOriginalFilename();
        String filePath=System.getProperty("user.dir")+"/temp";
        File dir=new File(filePath);
        if(!dir.exists()){
            dir.mkdir();
        }
        try{
            file.transferTo(dir);
        }catch (Exception e){
            log.error(e.getMessage(),e);
        }

    }

}

六.POI工具类

import lombok.extern.slf4j.Slf4j;

@Component
@Slf4j   //可以直接使用日志方法  log.error(...);
public class PoiUtil<T> {
    //根据文件后缀生成响应的工作簿,我这里只支持xlsx格式
    public  Workbook getWorkBook(MultipartFile file) throws Exception{
        String fileName = file.getOriginalFilename().toLowerCase();
        InputStream is = null;
        try {
            if (fileName.endsWith("xlsx")) {
                return new XSSFWorkbook(file.getInputStream());
            } else {
                throw new Exception("excel文件类型错误");
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new Exception("文件格式错误");
        } finally {
            try {
                if (is != null) {
                    {
                        is.close();
                    }
                }
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
        }
    }


    /**
     *  导入
     * @param workbook  工作簿
     * @param clazz  对应实体类
     * @return
     */
    public List<T> importExcel(Workbook workbook, Class<T> clazz){
        //利用反射获取我在注解里定义好的字段顺序
        String[] fields=clazz.getAnnotation(ExcelTitle.class).value();
        List<T> list=new ArrayList<>();  //返回的对象列表
        //获取第一个工作簿,只支持解析第一个工作簿
        Sheet sheet=workbook.getSheetAt(0);
        for(Row row:sheet){
            //第一次循环  表头跳过 
            if(row==sheet.getRow(0)){
                continue;
            }
            //第二次往后
            try {
                T t=clazz.newInstance();
                //row.getLastCellNum()获取的不是行数,是下标,所以+1
                for(int i=0;i<row.getLastCellNum()+1;i++){
                    Cell cell=row.getCell(i);
                    if(cell!=null){
                    	 //cell的值类型需要处理
                        String cellValue=getCellStringVal(cell);
                        //获取所有字段,包括private
                        Field f=clazz.getDeclaredField(fields[i]);
                        //设置为true后才能操作private属性
                        f.setAccessible(true);
                        //给该属性设置值
                        f.set(t,cellValue);
                    }
                }
                list.add(t);
            }catch (Exception e){
                log.error(e.getMessage(),e);
            }
        }
        return list;
    }

    //导出
    public void exportData(String fileName,List<T> list, HttpServletResponse response,Class<T> clazz){
        Field[] field=clazz.getDeclaredFields();
        Workbook workbook=new XSSFWorkbook();
        Sheet sheet=workbook.createSheet();
        Row row=null;
        try {
            //获取注解中定义好的中文表头顺序
            String[] title=clazz.getAnnotation(ExcelTitle.class).title();
            //获取注解中定义好的字段顺序
            String[] fields=clazz.getAnnotation(ExcelTitle.class).value();
            for(int i=0;i<=list.size();i++){
                row=sheet.createRow(i);
                Cell cell=null;
                if(i==0){ //第一次创建标题行
                    for(int j=0;j<title.length;j++){
                        cell=row.createCell(j);
                        //安顺序设置excel表头
                        cell.setCellValue(title[j]);
                    }
                    continue;
                }
                //第二次循环开始设置数据
                T t = list.get(i - 1);
                for(int j=0;j<fields.length;j++){
                    Field f=t.getClass().getDeclaredField(fields[j]);
                    f.setAccessible(true);
                    cell=row.createCell(j);
                    if(f.get(t)!=null) { //此条数据的该字段有值
                        cell.setCellValue(f.get(t).toString());
                    }
                }
            }
            //输出Excel文件
            OutputStream output=response.getOutputStream();
            response.reset();
            response.addHeader("Content-Disposition", "attachment;filename=" + fileName);
            response.setContentType("multipart/form-data");
            workbook.write(output);
            output.close();

        } catch (Exception e) {
            log.error(e.getMessage(),e);
        }
    }

    private String getCellStringVal(Cell cell) {
        CellType cellType = cell.getCellTypeEnum();
        switch (cellType) {
            case NUMERIC:
                if (DateUtil.isCellDateFormatted(cell)) {
                    return new SimpleDateFormat("yyyy-MM-dd").format(cell.getDateCellValue()); //日期型
                } else {
                    // 解决问题:1,科学计数法(如2.6E+10),2,超长小数小数位不一致(如1091.19649281798读取出1091.1964928179796),3,整型变小数(如0读取出0.0)
                    return NumberToTextConverter.toText(cell.getNumericCellValue());
                }
            case STRING:
                return cell.getStringCellValue();
            case BOOLEAN:
                return String.valueOf(cell.getBooleanCellValue());
            case FORMULA:
                return cell.getCellFormula();
            case BLANK:
                return "";
            case ERROR:
                return String.valueOf(cell.getErrorCellValue());
            default:
                return "";
        }
    }

七.结束
前后端分离,ajax请求不能下载文件 原因

我主要是通过自定义注解定义好excel需要的字段的顺序,poiUtil传入相应的实体Class,再通过反射获取,自定义注解当然也可以用返回字段顺序的方法替代,我只是学着用用自定义注解,有更灵活的方法还望指教,谢谢。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot是一个基于Spring框架的快速开发框架,而POI是一个Java处理Microsoft Office格式文件的开源库。通过结合Spring Boot和POI,我们可以实现Excel文件的导入导出功能。 在Spring Boot中使用POI进行Excel文件导入导出,需要先添加POI的依赖。在pom.xml文件中添加以下依赖: ``` <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>4.1.2</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>4.1.2</version> </dependency> ``` 接下来,我们可以使用POI提供的API来实现Excel文件的导入导出。具体实现方式可以参考以下代码: Excel文件导入: ``` public List<User> importExcel(MultipartFile file) throws IOException { List<User> userList = new ArrayList<>(); Workbook workbook = WorkbookFactory.create(file.getInputStream()); Sheet sheet = workbook.getSheetAt(); for (int i = 1; i <= sheet.getLastRowNum(); i++) { Row row = sheet.getRow(i); User user = new User(); user.setName(row.getCell().getStringCellValue()); user.setAge((int) row.getCell(1).getNumericCellValue()); user.setGender(row.getCell(2).getStringCellValue()); userList.add(user); } return userList; } ``` Excel文件导出: ``` public void exportExcel(List<User> userList, HttpServletResponse response) throws IOException { Workbook workbook = new XSSFWorkbook(); Sheet sheet = workbook.createSheet("用户列表"); Row headerRow = sheet.createRow(); headerRow.createCell().setCellValue("姓名"); headerRow.createCell(1).setCellValue("年龄"); headerRow.createCell(2).setCellValue("性别"); for (int i = ; i < userList.size(); i++) { Row row = sheet.createRow(i + 1); row.createCell().setCellValue(userList.get(i).getName()); row.createCell(1).setCellValue(userList.get(i).getAge()); row.createCell(2).setCellValue(userList.get(i).getGender()); } response.setContentType("application/vnd.ms-excel"); response.setHeader("Content-disposition", "attachment;filename=userList.xlsx"); workbook.write(response.getOutputStream()); } ``` 以上代码实现了一个简单的Excel文件导入导出功能。在实际开发中,我们可以根据具体需求进行修改和扩展。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值