批量导入数据(上传/解析excel)POI技术+EasyExcel导入、导出

Excel读写操作
使用Apache POI提供了程序对微软Office文档读写的操作

1.导入相关的jar包
2.读写Excel表,并插入几个数据,将表导入到项目根目录中

在这里插入图片描述
3.通过代码解析Excel和生成Excel

//Excel文件---HSSWorkbook(针对xls格式文件)工作薄对象----sheet----rows
HSSFWorkbook hssfWorkbook = new HSSFWorkbook(new FileInputStream("info.xls"));//获取工作簿对象
HSSFSheet sheet = hssfWorkbook.getSheet("Sheet1");//通过名称获取Sheet
HSSFSheet sheet = hssfWorkbook.getAt(0);//通过下标获取Sheet

//得到sheet,解析每一行
for(Row row:sheet){
	for(Cell cell:row){
		cell.getStringCellValue();//获取每个单元格的值,如果Excel单元格使用的是数字格式,不能使用GetStringCellValue
		cell.getNumberCellValue();//读取数字

		/*
			实际开发中采用判断单元格格式的方式
		*/
		if(cell.getCellType()==Cell.CELL_TYPE_STRING){
			cell.getStringCellValue();
		}else if(cell.getCellType()==Cell.CELL_CELL_TYPE_NUMERIC){
			cell.getNumbericCellValue();
		}
	}
}

鉴于以上的判断,所以可以在Excel单元格中右键以文本的格式进行存储

//打印指定单元格
row.getCell(1).getStringValue();//每行第二个单元格内容

生成Excel文件

//向C盘根目录生成一个Excel文件
HSSFWorkbook hssfWorkbook = new HSSFWorkbook(); //1.创建空工作簿
HSSFSheet sheet = hssfWorkbook.createSheet("数据信息"); //2.创建sheet
HSSFRow row = sheet.createRow(0); //3.创建行
row.createCell(0).setCellValue("产品"); //4.创建单元格,并填充数据
row.createCell(1).setCellValue("价格");

//将数据写入到磁盘上
hssfWorkbook.write(new FileOutputStream(C:/text.xls));

================================================================

导出

实体类

@Data
public class DictEeVo{
	
	@ExcelProperty(value="id",index = 0)
	private Long id;

	@ExcelProperty(value="上级id",index=1)
	private Long parantId;

	@ExcelProperty(value="名称",index=2)
	private String name;

	@ExcelProperty(value="值",index=3)
	private String value;

	@ExcelProperty(value="编码",index=4)
	private String dictCode;
	
}

Controller

//Controller 导出数据字典
@GetMapping("exportData")
public void exportDict(HttpServletResponse response){
	dictService.exportDictData(response);
}
//Service实现类
public void exportDictData(HttpServletResponse response){
	
	//设置下载信息
	response.setContentType("application/vnd.ms-excel");
	response.setharacterEncoding("utf-8");
	String fileName = URLEncoder.encode("数据字典","UTF-8");
	response.setHeader("Content-disposition","attachment;filename="+fileName+".xlsx");

	//查询数据库
	List<Dict> dictList = baseMapper.selectList(null);
	//将Dict->DictEeVo
	List<DictEeVo> dictVoList = new ArrayList<>();
	for(Dict dict:dictList){
		DictEeVo dictEeVo = new DictEeVo();
		BeanUtils.copyProperties(dict,dictEeVo);
		dictVoList.add(dictEeVo);
	}

	//调用EasyExcel方法进行写操作
	try{
		EasyExcel.write(response.getOutputStream(),DictEeVo.class).sheet("dict")
		.doWrite(dictVoList);
	}catch(	IOException e){
		e.printStackTrace();
	}
	
}

前端list.vue

<div class="el-toolbar">
	<div class="el-toolbar-body" style="justify-content:flex-start;">
		<el-button type="text" @click="exportData">
			<i class="fa fa-plus">导出
		</el-button>
	</div>
</div>

<javascript>
import dict from '@/api/dict'
export default{
	data(){
		return {
			list:[]
		}
	},
	created(){
		this.getDictList(1)
	},
	methods:{
		exportData(){
			//调用导出接口
			window.location.href="http://localhost:8202/admin/cmn/dict/exportData"
		}	
	}
}
	
</javascript>

导入

要求实体类的id必须取消自动添加(直接继承BaseMapper默认是自动增长)

Controller

@PostMapping("importData")
public Result importDict(MultipartFile file){
	dictService.importDictData(file);
	return Result.ok();
}

Service实现类

@Override
public void importDictData(MultipartFile file){
	
	try{
		EasyExcel.read(file.getInputStream(),DictEeVo.class,new DictListener(baseMapper)).sheet().doRead();
	}catch(IOException e){
		e.printStackTrace();
	}
}

Listener监听器

public class DictListener extends AnalysisEventListener<DictEeVo>{

	//通过构造的方式将mapper注入
	private DictMapper dictMapper;
	
	public DictListener(DictMapper dictMapper){
		this.dictMapper = dictMapper;
	}
	
	//一行一行读取,dictEeVo为读取后封装对象
	@Override
	public void invoke(DictEeVo dictEeVo,AnalysisContext analysisContext){
		Dict dict = new Dict();
		BeanUtils.copyProperties(dictEeVo,dict);
		dictMapper.insert(dict);
	}

	@Override
	public void doAfterAllAnalysed(AnalysisContext analysisContext){
		
	}
}

前端list.vue导入按钮

<el-button type="text" @click="importData"><i class="fa fa-plus"/>导入</el-button>

methods:{
	importData(){}
}

==============================================

SpringBoot自定义注解+反射实现 excel 导入的数据组装及字段校验

一、自定义注解

该注解主要标记相应字段与cell的对应关系

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface ImportValidation{
	
	//下标,与excel中列对应,从0开始  
    int index();  
    //是否必填,默认是必填  
    boolean nullAble() default true;  
    // 字典的Code,用于字典转换  
    String domainCode() default "";  
    //字典的名称,用于错误提醒  
    String name() default  "";  
}

二、定义一个公共的静态方法

该公共方法需要包含三个参数

  • class:用于组装数据
  • Map<Integer,String[]>:保存从excel中读取的全部内容
  • domainCodes:用于涉及的字段转换,调用方应将字段按照code组装成Map的形式以供使用
public static Result assembleExcelData(Class entryClass, Map<Integer, String[]> excelData,  
       Map<String,Object> domainCodes){

	//保存返回的结果  
    Result result = new Result();  
    //组装后的数据LIST  
    List<Object> returnList = new ArrayList<>();  
    //保存校验失败信息  
    StringBuilder errorMsg = new StringBuilder(); 

	//循环excel数据  
    excelData.forEach((i,cells)->{  
        Object vo = null;  
        try {  
            //按照传入的Class,生成对应实例  
            vo= entryClass.newInstance();  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
        //获取并循环Bean中的所有字段,进行校验和组装  
        for (Field field : entryClass.getDeclaredFields()) {  
            //如果包含有ImportValidation注解的话,才进行处理。  
            if (field.isAnnotationPresent(ImportValidation.class)) {  
                ImportValidation annotation = field.getAnnotation(ImportValidation.class);  
                //cell下表  
                int index = annotation.index();  
                //字典Code  
                String domainCode = annotation.domainCode();  
                //是否必填  
                boolean nullAble = annotation.nullAble();  
                //字段名称  
                String name = annotation.name();  
                //获取单元格内容,并前后去空格处理  
                String cellData = cells[index].trim();  
                /*如果字段为空,且字段设置不能为空,则进行错误提醒*/  
                try {  
                    //若必填,则进行判断校验并提醒  
                    if (StringUtils.isEmpty(cellData) && !nullAble) {  
                        errorMsg.append("第").append(i).append("行: ").append(name).append("字段不能为空!\r\n");  
                    }  
                    /*如果字典编码为空,则可以直接赋值*/  
                    else if (StringUtils.isEmpty(domainCode) || StringUtils.isEmpty(cellData)) {  
                        //给对应字段赋值  
                        setFiled(field, vo, cellData);  
                    } else {  
                       //进行字典转换  
                        List<Map> domains = (List<Map>) domainCodes.get(domainCode);  
                        boolean match = false;  
                        for (Map map : domains) {  
                            if (map.get("TEXT").equals(cellData)) {                                 
                                //给对应字段赋值  
                                setFiled(field, vo, String.valueOf(map.get("VALUE")));  
                                match = true;  
                                break;  
                            }  
                        }  
                        /*如果没有匹配,则转换失败*/  
                        if (!match) {  
                            errorMsg.append("第").append(i).append("行: ").append(name).append("字段字典值不存在!!\r\n");  
                        }  
                    }  
                } catch (Exception e) {  
                    errorMsg.append("第").append(i).append("行: ").append(name).append("字段填写格式不正确!!\r\n");  
                }  
  
            }  
        }  
        //组装LIST  
        returnList.add(vo);  
    });  
    //如果有错误信息的话,返回错误信息,返回错误标记  
    if (errorMsg.length()>0){  
        result = Result.buildError();  
        result.setMsg(errorMsg.toString());  
    }  
    //放入组装后的LIST。校验失败的字段值为空  
    result.setData(returnList);  
  
    return result;  
}  

//反射给Filed赋值  
    public static void setFiled(Field filed,Object vo,String data) throws IllegalAccessException {  
        try {  
            //当单元格值不为空的时候才需要进行赋值操作  
            if (StringUtils.isNotEmpty(data)){  
                //获取Bean 属性字段的类型  
                Type fileType = filed.getGenericType();  
                filed.setAccessible(true);  
                //如果是String  
                if (fileType.equals(String.class)){  
                    filed.set(vo,data);  
                }  
                //如果是int  
                else if(fileType.equals(int.class)||fileType.equals(Integer.class)){  
                    filed.set(vo,Integer.valueOf(data));  
                }  
                //如果是Double  
                else if(fileType.equals(Double.class)||fileType.equals(double.class)){  
                    filed.set(vo,Double.valueOf(data));  
                }  
                //如果是Long  
                else if(fileType.equals(Long.class)||fileType.equals(long.class)){  
                    filed.set(vo,Long.valueOf(data));  
                }  
                //如果是BigDecimal  
                else if(fileType.equals(BigDecimal.class)){  
                    filed.set(vo,new BigDecimal(data));  
                }  
                //如果是日期  
                else if(fileType.equals(Date.class)){  
                    filed.set(vo, DateUtils.parseIso8601DateTime(data));  
                }  
            }  
  
        } catch (Exception e) {  
            throw e;  
        }  
  
    }  

三、使用

如果校验失败的话是给前端返回一个错误提醒内容的txt文件。可自行根据项目情况处理。校验成功则做插入的操作。

String domainCodesStr = "MM_DIC_PART_ATTR,MM_DIC_PART_TYPE,MM_DIC_PART_BELONG,MM_DIC_BASE_UNIT," +  
        "MM_DIC_PART_SOURCE,MM_DIC_W_UNIT,MM_MIN_SHELF_LIFE_UNIT,MM_CURRENCY";  
/*查询相关字典,进行校验和转换*/  
Map<String, Object> domainsCodes = wsDataDomainService.getDataByDomainCodes(domainCodesStr.split(","));  
/*校验并组装数据*/  
Result result = ExcelUtils.assembleExcelData(MmPartNumber.class, excelData, domainsCodes);  
if (result.getCode() != 0) {  
    String realPath = SpringContextHolder.getServletContext().getRealPath("/");  
    String destination = realPath + "导入错误信息.txt";  
    /*返回错误信息文件*/  
    File file = new File(destination);  
    if (!file.exists()) {  
        file.createNewFile();  
    }  
  
    FileWriter fileWriter = new FileWriter(file);  
    fileWriter.write(result.getMsg());  
    fileWriter.close();  
    HttpServletResponse response = context.getHttpServletResponse();  
    FileDownload.fileDownload(response, realPath + "导入错误信息.txt", "导入错误信息.txt");  
  
} else {  
//TODO BatchInsert  
}  

效果
在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
实现上传excel文件并将数据传输到数据库的步骤如下: 1. 前端实现文件上传功能:使用Vue.js开发前端页面,使用element-ui组件库实现文件上传组件。具体实现可以参考element-ui的文档和示例代码。 2. 后端实现文件上传功能:使用Spring Boot框架开发后端接口,使用Apache POI或者EasyExcel解析Excel文件并将数据存入数据库。具体实现可以参考Spring Boot官方文档和EasyExcel的官方文档。 3. 前后端交互:前端页面通过Ajax请求后端接口上传文件,并将文件数据以form-data格式传输到后端。后端接口接收到请求后,解析Excel文件并将数据存入数据库,最后返回上传结果给前端。 下面是一个简单的示例代码,仅供参考: 前端代码: ```vue <template> <el-upload class="upload-demo" drag action="/api/upload" :before-upload="beforeUpload" :on-success="onSuccess" :on-error="onError" > <i class="el-icon-upload"></i> <div class="el-upload__text">将 Excel 文件拖到此处,或点击上传</div> <div class="el-upload__tip" slot="tip">仅支持 .xls 和 .xlsx 格式的 Excel 文件</div> </el-upload> </template> <script> export default { methods: { beforeUpload(file) { const isExcel = file.type === 'application/vnd.ms-excel' || file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'; if (!isExcel) { this.$message.error('只能上传 .xls 或 .xlsx 格式的 Excel 文件'); } return isExcel; }, onSuccess(response) { if (response.code === 0) { this.$message.success('上传成功'); } else { this.$message.error(`上传失败: ${response.msg}`); } }, onError(error) { this.$message.error(`上传失败: ${error.message}`); }, }, }; </script> ``` 后端代码: ```java @RestController @RequestMapping("/api") public class UploadController { @PostMapping("/upload") public Result<?> upload(@RequestParam("file") MultipartFile file) throws IOException { if (file.isEmpty()) { return Result.error("上传失败: 文件为空"); } String filename = file.getOriginalFilename(); String ext = FilenameUtils.getExtension(filename); if (!"xls".equals(ext) && !"xlsx".equals(ext)) { return Result.error("上传失败: 仅支持 .xls 或 .xlsx 格式的 Excel 文件"); } List<User> userList = new ArrayList<>(); Workbook workbook = WorkbookFactory.create(file.getInputStream()); Sheet sheet = workbook.getSheetAt(0); for (int i = 1; i <= sheet.getLastRowNum(); i++) { Row row = sheet.getRow(i); if (row == null) { continue; } User user = new User(); user.setName(row.getCell(0).getStringCellValue()); user.setAge((int) row.getCell(1).getNumericCellValue()); user.setGender(row.getCell(2).getStringCellValue()); userList.add(user); } userService.saveAll(userList); return Result.ok(); } } ``` 上述代码中,`UploadController`是一个Spring MVC的控制器类,用于处理上传文件的请求。`upload`方法接收一个`MultipartFile`类型的参数,即前端上传的文件数据。在方法中,我们首先判断文件是否为空,然后根据文件的扩展名判断是否为Excel文件。如果不是Excel文件,则返回上传失败的结果。否则,我们使用Apache POI解析Excel文件,将数据转换成`User`对象并存入数据库。最后,返回上传成功的结果。 需要注意的是,上述代码中的`User`对象是一个自定义的Java类,用于存储Excel中的数据。在实际开发中,需要根据实际情况定义相应的Java类来存储数据。同时,还需要在Spring Boot的配置文件中配置数据库连接信息、数据源等相关信息。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值