代码 :https://github.com/goodboyQAQ/poi
一.pom文件
org.apache.poi
poi
3.15
org.apache.poi
poi-ooxml-schemas
3.15
org.apache.poi
poi-ooxml
3.15
org.projectlombok
lombok
true
二.创建实体类
Result类保存一些数据信息返回给前端
@Data //生成 getting 和 setting ,equals、canEqual、hashCode、toString 方法
public class Result {
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 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 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 list) {
if(list.size()==0){
return;
}
List insertList=new ArrayList<>();
List 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 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 {
//根据文件后缀生成响应的工作簿,我这里只支持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 importExcel(Workbook workbook, Class clazz){
//利用反射获取我在注解里定义好的字段顺序
String[] fields=clazz.getAnnotation(ExcelTitle.class).value();
List 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 list, HttpServletResponse response,Class 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
七.结束
前后端分离,ajax请求不能下载文件 原因
我主要是通过自定义注解定义好excel需要的字段的顺序,poiUtil传入相应的实体Class,再通过反射获取,自定义注解当然也可以用返回字段顺序的方法替代,我只是学着用用自定义注解,有更灵活的方法还望指教,谢谢。