目录
- 前言
- 一、需求一:数据库查询的数据导出成Excel表格
- 1.1 Vue前端实现导出按钮+点击事件
- 1.2 后端根据数据库查询结果生成xlsx文件
- 二、需求二:对生成的xlsx文件调用打印机打印
- 2.1 Vue前端实现按钮+事件
- 2.2 后端实现打印
前言
最近在弄一个需求,需求如下:
需求一:Vue前端点击导出按钮,就可以将从数据库查出的数据导出成xlsx,即Excel表格形式
需求二:点击打印按钮,要将生成的xlsx文件启动进程,调用打印接口,实现打印,这是为了生成客户订单表,可以让机器直接打印
PS:想吐槽网上很多都复制粘贴的技术文章,找半天一点卵用都没用,这里记录一下自己解决方案,若转发请注明出处,原创不易,有问题可评论区留言
需求一效果预览
需求二预览
一、需求一:数据库查询的数据导出成Excel表格
1.1 Vue前端实现导出按钮+点击事件
<el-button type="success" round icon="el-icon-download" @click="exportByEasyPoi">导出</el-button>
对应按钮点击触发的exportByEasyPoi方法
,这里我用的是EasyPoi操作Excel,也挺多公司用的
注:前端是Vue2
<script>
import userApi from '@/api/userManage'
export default {
methods: {
exportByEasyPoi() {
userApi.exportByEasyPoi().then(response => {
this.$message.success('导出成功')
})
}
}
}
</script>
这里我调用了src/api/userManage.js
,这是自己创建的js脚本,调用后端接口的
import request from '@/utils/request'
export default {
exportByEasyPoi() {
return request({
url: `/user/exportep`,
method: 'get',
timeout: '10000'
})
}
}
需求一前端就这么多了,接下来就是后端
1.2 后端根据数据库查询结果生成xlsx文件
首先导入EasyPoi相关依赖
<!--easypoi-->
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-spring-boot-starter</artifactId>
<version>4.3.0</version>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-base</artifactId>
<version>4.3.0</version>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-web</artifactId>
<version>4.3.0</version>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-annotation</artifactId>
<version>4.3.0</version>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
下面先给出后端核心逻辑代码,后面会具体讲解,这里就不加篇幅,直接把逻辑代码写在Controller层,原本应该写在Service层,即xxxServiceImpl实现类中,这里为了方便,直接这么写下。
@Autowired
private IUserService userService;
@GetMapping("/exportep")
public Result<?> exportByEasyPoi(HttpServletResponse response) throws IOException {
// mybatisplus实现的list(),拿到用户信息列表
List<User> users = userService.list();
List<ExcelUserVo> excelUserVos = BeanCopyUtils.copyBeanList(users, ExcelUserVo.class);
// easypoi导出
ExportParams params = new ExportParams();
params.setTitle("测试");
// 表格左下角的sheet名称
params.setSheetName("用户信息");
Workbook workbook = ExcelExportUtil.exportExcel(params, ExcelUserVo.class, excelUserVos);
// 判断文件路径是否存在,并将信息写入文件
try{
// 文件夹是否存在,若没有对应文件夹直接根据路径生成文件会报错
File folder = new File(Constants.XLSX_DIR);
if (!folder.exists() && !folder.isDirectory()) {
folder.mkdirs();
}
// 文件是否存在
File file = new File(Constants.XLSX_DIR + "\\" + Constants.XLSX_NAME);
if (!file.exists()){
file.createNewFile();
}
// 输出流写入
FileOutputStream outputStream = new FileOutputStream(Constants.XLSX_DIR + "\\" + Constants.XLSX_NAME);
workbook.write(outputStream);
// 关闭写,不然用户点击生成的文件会显示只读
outputStream.close();
workbook.close();
}catch (IOException e){
e.printStackTrace();
}
return Result.success("导出成功");
}
ExcelUserVo.java
是自己需要显示在表格中的列,就是我们查询用户表,大部分时候不需要展示所有的属性到前端,挑需要显示的,所以封装成xxxVo,比如密码、创建时间、备注可以不显示在前端,不用打印就不要。
import cn.afterturn.easypoi.excel.annotation.Excel;
import cn.afterturn.easypoi.excel.annotation.ExcelTarget;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@NoArgsConstructor
@AllArgsConstructor
@ExcelTarget("sys_user")
public class ExcelUserVo implements Serializable {
@Excel(name = "用户id", width = 10)
// @ExcelIgnore // 表示忽略, 不在表格生成
private Integer id;
@Excel(name = "用户名", width = 20)
private String username;
@Excel(name = "邮箱", width = 30)
private String email;
@Excel(name = "电话", width = 30)
private String phone;
@Excel(name = "状态(1:正常,0:禁用)", width = 10)
private Integer status;
}
BeanCopyUtils.java
是常用工具类,可以实现对象的拷贝,将两个对象中相同属性字段拷贝
import java.util.List;
import java.util.stream.Collectors;
public class BeanCopyUtils {
private BeanCopyUtils() {
}
/** 单个对象*/
public static <V> V copyBean(Object source, Class<V> clazz) {
/** 创建目标对象 实现属性拷贝*/
V result = null;
try {
result = clazz.newInstance();
/** 拷贝*/
BeanUtils.copyProperties(source, result);
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/** 集合*/
public static <O, V> List<V> copyBeanList(List<O> list, Class<V> clazz) {
/** 创建目标对象 实现属性拷贝*/
return list.stream()
.map(o -> copyBean(o, clazz))
.collect(Collectors.toList());
}
}
自定义常量Constants.java
,这里主要是定义输出路径
public class Constants {
// excel表
public static final String XLSX_DIR = "D:\\study\\excel";
public static final String XLSX_NAME = "easypoi.xlsx";
}
数据库sys_user表结构
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL,
`password` varchar(100) DEFAULT NULL,
`email` varchar(50) DEFAULT NULL,
`phone` varchar(20) DEFAULT NULL,
`status` int(1) DEFAULT NULL,
`avatar` varchar(200) DEFAULT NULL,
`deleted` int(1) DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
二、需求二:对生成的xlsx文件调用打印机打印
2.1 Vue前端实现按钮+事件
<el-button type="warning" round icon="el-icon-printer" @click="printExcel">打印</el-button>
methods: {
printExcel() {
userApi.printTable().then(response => {
this.$message.success('请求打印成功')
})
}
}
userManage.js
中再写一个调用后端接口的
export default {
printTable() {
return request({
url: `/user/print`,
method: 'get',
timeout: '100000'
})
}
}
2.2 后端实现打印
这里也干脆实现逻辑不写在Service层了,省点字,实际开发中还是要遵循规范哈
@GetMapping("/print")
public Result<?> printInfo(){
try {
String filepath = Constants.XLSX_DIR + "\\" + Constants.XLSX_NAME;
PrintUtil.print(filepath);
} catch (Exception e) {
e.printStackTrace();
}
return Result.success();
}
核心打印工具PrintUtil.java
,这个java文件稍微有点长
import com.jacob.activeX.ActiveXComponent;
import com.jacob.com.ComThread;
import com.jacob.com.Dispatch;
import com.jacob.com.Variant;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;
import javax.imageio.ImageIO;
import javax.print.*;
import javax.print.attribute.DocAttributeSet;
import javax.print.attribute.HashDocAttributeSet;
import javax.print.attribute.HashPrintRequestAttributeSet;
import javax.print.attribute.standard.OrientationRequested;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
public class PrintUtil {
/**
* 竖屏模式
*/
public static OrientationRequested PORTRAIT = OrientationRequested.PORTRAIT;
/**
* 横屏模式
*/
public static OrientationRequested LANDSCAPE = OrientationRequested.LANDSCAPE;
/**
* 获取全部打印设备信息
* @return 返回全部能用的打印服务的List
*/
public static List<PrintService> getDeviceList() {
// 构建打印请求属性集
HashPrintRequestAttributeSet pras = new HashPrintRequestAttributeSet();
// 设置打印格式,因为未确定类型,所以选择autosense
DocFlavor flavor = DocFlavor.BYTE_ARRAY.AUTOSENSE;
// 查找所有的可用的打印服务
PrintService printService[] = PrintServiceLookup.lookupPrintServices(flavor, pras);
List<PrintService> list = Arrays.asList(printService);
return list;
}
/**
* 根据文件类型不同调用不同代码去打印
* @param filePath 文件路径
*/
public static void print(String filePath) throws Exception {
PrintService printService = PrintServiceLookup.lookupDefaultPrintService();
String defaultDeviceName = printService.getName();
print(filePath, defaultDeviceName);
}
/**
* 额外传入一个 AfterPrint,会在打印完成后调用 afterPrint.run()
* @param filePath
* @param afterPrint
* @throws Exception
*/
public static void print(String filePath, AfterPrint afterPrint) throws Exception {
print(filePath);
afterPrint.run();
}
/**
* 根据文件类型不同调用不同代码去打印
* @param filePath 文件路径
* @param deviceName 设备名称,传入哪个设备的名称,就让哪个设备去打印
*/
public static void print(String filePath, String deviceName) throws Exception{
List<PrintService> list = getDeviceList();
PrintService printService = null;
for (PrintService p : list) {
if(p.getName().equals(deviceName)) {
printService = p;
break;
}
}
if(printService == null) {
throw new Exception("Device not found");
}
String type = filePath.replaceAll(".*\\.","");
if("jpg".equalsIgnoreCase(type)) {
normalPrint(new File(filePath), DocFlavor.INPUT_STREAM.JPEG, printService);
return;
}
if("jpeg".equalsIgnoreCase(type)) {
normalPrint(new File(filePath), DocFlavor.INPUT_STREAM.JPEG, printService);
return;
}
if("gif".equalsIgnoreCase(type)) {
normalPrint(new File(filePath), DocFlavor.INPUT_STREAM.GIF, printService);
return;
}
if("pdf".equalsIgnoreCase(type)) {
printPDF(new File(filePath), DocFlavor.INPUT_STREAM.PNG, printService);
return;
}
if("png".equalsIgnoreCase(type)) {
normalPrint(new File(filePath), DocFlavor.INPUT_STREAM.PNG, printService);
return;
}
if("doc".equalsIgnoreCase(type)) {
printWord(filePath, deviceName);
return;
}
if("docx".equalsIgnoreCase(type)) {
printWord(filePath, deviceName);
return;
}
if("xls".equalsIgnoreCase(type)) {
printExcel(filePath, deviceName);
return;
}
if("xlsx".equalsIgnoreCase(type)) {
printExcel(filePath, deviceName);
return;
}
if("ppt".equalsIgnoreCase(type)) {
printPPT(filePath, deviceName);
return;
}
if("pptx".equalsIgnoreCase(type)) {
printPPT(filePath, deviceName);
return;
}
}
/**
* 会在打印完成后调用 afterPrint.run()
* @param filePath
* @param deviceName
* @param afterPrint
* @throws Exception
*/
public static void print(String filePath, String deviceName, AfterPrint afterPrint) throws Exception{
print(filePath, deviceName);
afterPrint.run();
}
/**
* javase的打印机打印文件,支持jpg,png,gif,pdf等等
* @param file 要打印的文件
* @param flavor 打印格式
*/
private static void normalPrint(File file, DocFlavor flavor) {
// 定位默认的打印服务
PrintService service = PrintServiceLookup
.lookupDefaultPrintService(); // 显示打印对话框
normalPrint(file, flavor, service);
}
private static void normalPrint(File file, DocFlavor flavor, PrintService service) {
normalPrint(file, flavor, PORTRAIT, service);
}
/**
* javase的打印机打印文件,支持jpg,png,gif等等
* @param file 要打印的文件
* @param service 打印机选择
* @param requested 设定横屏还是竖屏
* @param flavor 打印格式
*/
private static void normalPrint(File file, DocFlavor flavor, OrientationRequested requested, PrintService service) {
// 构建打印请求属性集
HashPrintRequestAttributeSet pras = new HashPrintRequestAttributeSet();
pras.add(requested);
if (service != null) {
try {
DocPrintJob job = service.createPrintJob(); // 创建打印作业
FileInputStream fis = new FileInputStream(file); // 构造待打印的文件流
DocAttributeSet das = new HashDocAttributeSet();
Doc doc = new SimpleDoc(fis, flavor, das);
job.print(doc, pras);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 打印pdf的方法,因为java内置的打印pdf的方法有病,所以首先需要把pdf转换成png,然后打印png
* @param file 要打印的文件
* @param flavor 要打印的文件
* @param service 打印设备
*/
private static void printPDF(File file, DocFlavor flavor, PrintService service) {
try {
PDDocument doc = PDDocument.load(file);
PDFRenderer renderer = new PDFRenderer(doc);
int pageCount = doc.getNumberOfPages();
for(int i=0;i<pageCount;i++){
File f = new File(file.getParent() + File.separator + "temp_" + i + ".png");
BufferedImage image = renderer.renderImageWithDPI(i, 96);
ImageIO.write(image, "PNG", f);
normalPrint(f, flavor, LANDSCAPE, service);
f.delete();
}
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 打印机打印Word
* @param filepath 打印文件路径
* @param deviceName 传入哪个设备名称,用哪个设备打印
*/
private static void printWord(String filepath, String deviceName) {
if(filepath.isEmpty()){
return;
}
ComThread.InitSTA();
//使用Jacob创建 ActiveX部件对象:
ActiveXComponent word=new ActiveXComponent("Word.Application");
//打开Word文档
Dispatch doc=null;
Dispatch.put(word, "Visible", new Variant(false));
word.setProperty("ActivePrinter", new Variant(deviceName));
Dispatch docs=word.getProperty("Documents").toDispatch();
doc=Dispatch.call(docs, "Open", filepath).toDispatch();
try {
Dispatch.call(doc, "PrintOut");//打印
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
if(doc!=null){
//关闭文档
Dispatch.call(doc, "Close",new Variant(0));
}
} catch (Exception e2) {
e2.printStackTrace();
}
word.invoke("Quit", new Variant[] {});//关闭进程
//释放资源
ComThread.Release();
}
}
/**
* 打印Excel
* @param filePath 打印文件路径,形如 E:\\temp\\tempfile\\1494607000581.xls
* @param deviceName 传入哪个设备名称,用哪个设备打印
*/
private static void printExcel(String filePath, String deviceName){
if(filePath.isEmpty()){
return;
}
ComThread.InitSTA();
ActiveXComponent xl=new ActiveXComponent("Excel.Application");
try {
Dispatch.put(xl, "Visible", new Variant(true));
Dispatch workbooks = xl.getProperty("Workbooks").toDispatch();
Dispatch excel=Dispatch.call(workbooks, "Open", filePath).toDispatch();
Dispatch.callN(excel,"PrintOut",new Object[]{Variant.VT_MISSING, Variant.VT_MISSING, new Integer(1),
new Boolean(false), deviceName, new Boolean(true),Variant.VT_MISSING, ""});
Dispatch.call(excel, "Close", new Variant(false));
} catch (Exception e) {
e.printStackTrace();
} finally{
xl.invoke("Quit",new Variant[0]);
ComThread.Release();
}
}
/**
* 打印PPT
* @param filePath
* @param deviceName
*/
private static void printPPT(String filePath, String deviceName) {
File file = new File(filePath);
File pdfFile = new File(file.getParentFile().getAbsolutePath() + file.getName() + ".pdf");
ActiveXComponent app = null;
Dispatch ppt = null;
try {
ComThread.InitSTA();
app = new ActiveXComponent("PowerPoint.Application");
Dispatch ppts = app.getProperty("Presentations").toDispatch();
ppt = Dispatch.call(ppts, "Open", filePath, true, true, false).toDispatch();
Dispatch.call(ppt, "SaveAs", pdfFile.getAbsolutePath(), 32);
} catch (Exception e) {
e.printStackTrace();
throw e;
} finally {
if (ppt != null) {
Dispatch.call(ppt, "Close");
}
if (app != null) {
app.invoke("Quit");
}
ComThread.Release();
}
try {
print(pdfFile.getAbsolutePath(), deviceName);
pdfFile.delete();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 接口,在打印结束后调用
*/
public interface AfterPrint {
void run();
}
}
PrintUtil.java中依赖两个打印需要的非常重要包,jacob和pdfbox,下面给出免费链接,阿里云盘不限速的
链接:Java打印相关依赖包
下载后,需要导入一下,也十分简单,如图
然后运行后端,点击按钮就会有对应效果了,如果你没有打印机,会弹出生成PDF,有打印机连着,会使用默认打印机,或者通过PrintUtil中方法自定义打印机名称,选择打印机打印
以上就是全部内容,过程有啥问题可以评论区和大家一起交流!