先前有聊过上传下载CSV文件时,挖了个Excel的坑,今天咱们就填一下.首先咱们使用的是阿帕奇的POI库,
gradle: compile("org.apache.poi:poi-ooxml:4.1.0")
在学习之前建议有必要回读前面的导出三连其中些知识点不在侧重,这里仅叙述实现及调整实现自己的业务,先来看一下上传.xlsx文件模板的样子:
硬编码如下:
/**
* @param fileName 导出文件名
* @param datas 导出数据集合
* @param onlyWrite default: 仅导出
* @param fontName 设置字体 default: 宋体
*/
fun write(fileName: String, datas: List<T>, onlyWrite: Boolean = true, fontName: String = "宋体") =
XSSFWorkbook().use { workbook ->
val createHelper = workbook.creationHelper
val sheet = workbook.createSheet(fileName)
sheet.defaultRowHeight = (sheet.defaultRowHeight * 2).toShort()
sheet.defaultColumnWidth = sheet.defaultColumnWidth * 2
val headerFont = workbook.createFont()
headerFont.fontName = fontName
val headerCellStyle = workbook.createCellStyle()
headerCellStyle.setFont(headerFont)
val cellStyle = workbook.createCellStyle()
cellStyle.dataFormat = createHelper.createDataFormat().getFormat("@") //设置单元格为文本
//设置附加属性dataFormat
for (i in (0..providers.size + 26)) { //最大列数x+26 设置此区域为文本,26为自由列数,用户导入时可填属性值
sheet.setDefaultColumnStyle(i, cellStyle)
}
var titleRow: XSSFRow //标题行
var rowIdx = 3 //数据开始行
val filedRow = sheet.createRow(0)
filedRow.ctRow.hidden = true //隐藏域名行
titleRow = sheet.createRow(1)
val infoRow = sheet.createRow(2)
infoRow.height = (3 * sheet.defaultRowHeight).toShort() //说明行高度设置
if (onlyWrite) { // 如果仅导出覆盖域名
titleRow = sheet.createRow(0)
rowIdx = 1 //数据开始行为1
}
titleRow.height = sheet.defaultRowHeight
// Header
datas.forEach {
val row = sheet.createRow(rowIdx++)
row.height = sheet.defaultRowHeight
providers.forEachIndexed { i, p ->
//域值
val cellFiled = filedRow.createCell(i)
cellFiled.setCellValue(p.field)
cellFiled.cellStyle = headerCellStyle
//标题
val cellTitle = titleRow.createCell(i)
if (p.required) { // 必填
cellTitle.setCellValue(p.title.plus("*")) //必填项加*标识
cellTitle.cellStyle = workbook.createCellStyle().apply {
this.setFont(workbook.createFont().also {
it.fontName = fontName
it.color = Font.COLOR_RED //必填项字体红色高亮
})
}
} else {
cellTitle.setCellValue(p.title)
}
//说明
headerCellStyle.wrapText = true
val infoCell = infoRow.createCell(i)
infoCell.setCellValue(createHelper.createRichTextString(p.information))
headerFont.color = IndexedColors.GREY_50_PERCENT.index
infoCell.cellStyle = headerCellStyle
//数据
val rc = row.createCell(i)
rc.setCellValue(p.func!!.apply(it).toString())
rc.cellStyle = cellStyle
}
}//.createTempFile(fileName, XLSX)
File.createTempFile(fileName, XLSX).apply {//创建文件时可指出文件地址("/home/xxx/Desktop/customers.xlsx"),这里是临时文件
FileOutputStream(this).use {
workbook.write(it) //写出文件
}
}
}
心细的同学可能观察到了,excel的第一行(index:0)给隐藏了,这里开启了: filedRow.ctRow.hidden = true //隐藏域名行,硬编码中所提到的26是指从第D列往后26列均为用户自由可添加数据值,暂时还没找到设置excel全部(a+All)列为文本的方式.
使用:
在此之前再次提醒复习导出三连,前面所提到的要用先注册,这里也做了增强:
/**
* 注册需要导入的数据属性及数值
* @param field 数据属性;若导入模板对象包含对象时filed需驼峰规则设置包含对象的属性名并拼接包含对象的内容属性名称
* 模板对象及包含对象所要导入数据属性必须初始化默认值(构造函数)
* 如: class A (var b: B = B()); class B (var c: String="", var d: Int = 0)部分属性可如下设置:
* filed: "bC", func: Function { it.b.c }
* @param required 必填字段设置
* @param information 属性格式等要求说明
*/
fun register(
field: String, title: String = "", func: Function<T, Any> = Function { "" }, required: Boolean = false, information: String = ""
) = providers.add(Provider(field, title, func, required, information)).let { this }
其实增强中所述的field驼峰命名规则是基于导入时相对而言,这里测试的导入方法中用到了ModelMapper映射,如果您欣喜于反射亦可要求您所习惯的方式如:a.b.c的链式调用取值,接下来也会说明下反射方法的实现.
导出或下载:
/**
* 数据下载及模板导出
* @param onlyDownload 默认仅用于数据下载,下载导出模板时设置true
* @param fontName 设置字体名称 默认宋体
*/
fun download(response: HttpServletResponse, fileName: String, datas: List<T>, onlyDownload: Boolean = true, fontName: String = "宋体") {
DownloadDataUtils.download(response, this.write(fileName, datas, onlyDownload, fontName), fileName.plus(XLSX))
}
需要说明的是测试我只支持XLSX格式.xls如果您喜欢可自行尝试.
测试:
@Throws(IOException::class)
fun main(args: Array<String>?) {
ExcelFileUtil<ReadExcel>().register("customerId", "编码", Function { it.customer.id }, true)
.register("customerName", "姓名", Function { it.customer.name }, true, "姓名必须不能为空;\n必须含有中文字符")
.register("customerGender", "性别", Function { it.customer.address }).register("customerAge", "年龄", Function { it.customer.age })
.write("customers", readExcels, false)
println(ExcelFileUtil<ReadExcel>().read(ReadExcel::class.java, 3, 4, FileInputStream(File("/home/xxx/Desktop/customers.xlsx")), "customers"))
}
这里我直接把文件写在了桌面,而不是经过浏览器(http协议),所以直接调用时需注意.