前言
java操作excel表格可以使用POI,但是我觉得这些太麻烦了。如果说我想实现合并单元格等操作,可能会更加麻烦。所以我就找到了Xdoc这个第三方API。
一、XDOC
Xdoc官网:http://www.xdocin.com/index.html,具体的使用方法可以自己看看,我就不赘述了。
一、excel模板
在xdoc官网的最下方有一个关于excel的模板,可以下载模板、填充数据后的表格以及实现代码。
但是我想结合springboot 使用,所以还是不能直接使用官方提供的代码的。但是excel模板可以参考一下。
特别注意的是重复数据的第一个单元格一定要加一个批注,这个批注和代码里的参数key值相同,下面会提到。
二、封装工具类
xdoc可以本地jar包调用也可以http请求调用。首先,我们下载官方提供的jar包:http://www.xdocin.com/XDocService.jar,由于maven远程仓库没有响应的jar包,所以就需要我们自己手动打进本地maven仓库,打包命令在以前的博客 SpringBoot使用银联支付 里提到过,可以参考一下。
接下来就是封装一个工具类。
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import org.apache.commons.codec.binary.Base64;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Component;
import com.hg.xdoc.XDocService;
import com.yuyi.lwq.tool.HttpUtils;
public class XdocClient {
private static final String XDOC_HOST = "http://www.xdocin.com/";
private static final String XDOC_PATH = "xdoc";
private static final String KEY = "自己的key";
/**
* word文档预览
* @param docUrl
* @return
*/
public static String preview(String docUrl){
return "http://www.xdocin.com/xdoc?_func=to&_format=html&_cache=true&_xdoc="+docUrl;
}
/**
* 转换文档格式
* @param docUrl
* @return
*/
public static String convert(String docUrl,Format format){
String result = null;
Map<String, String> param = new HashMap<String, String>();
param.put("_func", "to");
param.put("_key", KEY);
param.put("_xdoc", docUrl);
param.put("_format", format.toString());
//param.put("_to", "d:/b.pdf");
try {
HttpResponse response = HttpUtils.doGet(XDOC_HOST, XDOC_PATH, null, param);
HttpEntity entity = response.getEntity();
int code = response.getStatusLine().getStatusCode();
if (Objects.equals(code,200)) {
byte[] byteArray = EntityUtils.toByteArray(entity);
result = Base64.encodeBase64String(byteArray);
}
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 通过文件模板生成带数据的文件
* @param docUrl 文件,可以是本地文件,也可以是网络文件
* @param param 文件模板里对应的参数
* @param fileName 生成的文件的名字,生成地址为:项目/source/fileName
* @return
*/
public static boolean localGenerate(String docUrl,Map<String,Object> param,String fileName){
boolean isSuccess = false;
XDocService service = new XDocService();
try {
service.run(docUrl, param, new File(System.getProperty("user.dir")+"/source/"+fileName));
isSuccess = true;
} catch (IOException e) {
e.printStackTrace();
}
return isSuccess;
}
public enum Format{
DOCX{
@Override
public String toString() {
return "docx";
}
},
XLSX{
@Override
public String toString() {
return "xlsx";
}
},
PPTX{
@Override
public String toString() {
return "pptx";
}
},
PDF{
@Override
public String toString() {
return "docx";
}
},
HTML{
@Override
public String toString() {
return "html";
}
};
}
}
其中HttpUtil在之前的博客里写过
System.getProperty("user.dir")+"/source/"+fileName 是模板填充数据后生成的文件的位置
三、调用
接下来就可以在service层调用了
public ResultDTO<JSONObject> download() throws Exception {
//根据类别查询数据并生成json格式字符串
List<GoodsSaleDTO> breadList = goodsSaleLWQDAO.listAllGoodsSaleByClassify(1);
String bread = JSONObject.toJSONString(breadList);
List<GoodsSaleDTO> drinkList = goodsSaleLWQDAO.listAllGoodsSaleByClassify(2);
String drink = JSONObject.toJSONString(drinkList);
List<GoodsSaleDTO> iceList = goodsSaleLWQDAO.listAllGoodsSaleByClassify(3);
String ice = JSONObject.toJSONString(iceList);
List<GoodsSaleDTO> hotList = goodsSaleLWQDAO.listAllGoodsSaleByClassify(4);
String hot = JSONObject.toJSONString(hotList);
List<GoodsSaleDTO> otherList = goodsSaleLWQDAO.listAllGoodsSaleByClassify(5);
String other = JSONObject.toJSONString(otherList);
LocalDate now = LocalDate.now();
//模板需要的参数
Map<String, Object> param = new HashMap<String, Object>();
param.put("日期",LocalDateTimeTool.localDateToString(now, "yyyy年 MM月 dd日"));
param.put("面包类", bread);
param.put("饮品类", drink);
param.put("雪糕类", ice);
param.put("热饮类", hot);
param.put("其他", other);
String fileName = "休闲吧出库单 "+now.toString()+".xlsx";
boolean isSuccess = XdocClient.localGenerate("模板文件地址", param,fileName);
if (!isSuccess) {
return ResultDTO.error(-1);
}
String pathName = System.getProperty("user.dir")+"/source/"+fileName;
File file = new File(pathName);
InputStream in = new FileInputStream(file);
String result = FileTool.inputStream2String(in);
JSONObject res = new JSONObject();
res.put("fileName",fileName);
res.put("file", result);
return ResultDTO.ok(res);
}
其中输入流转String的工具类如下
public class FileTool {
public static String inputStream2String(InputStream in) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int len = 0;
byte[] b = new byte[1024];
while ((len = in.read(b, 0, b.length)) != -1) {
baos.write(b, 0, len);
}
byte[] buffer = baos.toByteArray();
//base64加密
return Base64.encodeBase64String(buffer);
}
}
这样,先是在工程目录下生成文件,然后用文件输入流读取,再转为字符串。controller层调用后,string转为byte字节数组,直接返给前端页面就可以下载了。
public ResponseEntity<Object> download()throws Exception{
ResultDTO<JSONObject> result = goodsSaleService.download();
Integer status = result.getStatus();
//404
if (Objects.equals(status,0)) {
return ResponseEntity.notFound().build();
}
//出错
if (Objects.equals(status, -1)) {
return ResponseEntity.badRequest().build();
}
JSONObject json = result.getData();
byte[] decodeData = Base64.decodeBase64(json.getString("file"));
HttpHeaders headers = new HttpHeaders();
headers.setCacheControl("no-cache, no-store, must-revalidate");
headers.setContentDispositionFormData("attachment",new String(json.getString("fileName").getBytes("utf-8"), "ISO-8859-1"));
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
//headers.setContentLength(file.getContentLength());
headers.setPragma("no-cache");
return new ResponseEntity<Object>(decodeData, headers, HttpStatus.CREATED);
}
写在最后的话
虽然是可以下载了,但是项目里还会有一份文件,时间长了之后,就会占用空间,所以我们可以写个方法删除或者定期清理。
File rootfile = new File(System.getProperty("user.dir")+"/source");
File[] files = rootfile.listFiles();
if (files.length != 0) {
for (File file : files) {
file.delete();
}
}