vue+java建立word和pdf文件(XWPFDocument)
阿新 • • 發佈:2022-02-16
A前端程式碼
1.線上檢視按鈕和方法
在診斷報告管理中,新增線上檢視的按鈕,新增點選事件@click="lookUp"
<template slot="menuLeft"> <!--線上檢視的按鈕--> <el-button size="small" class="el-button--primary" v-if="see" @click="lookUp" icon="el-icon-view">{{$t('button.reportView')}}</el-button> </template>
後面的methods裡,有線上查件具體的方法:this.$router.resolve是固定寫法,就是開啟一個新頁面。
//線上檢視方法 lookUp:function(){ if (!this.radio) { this.$message.error(this.$t('tips.messageDel')); }else{ let routeData = this.$router.resolve({ path:'/showDiagnose',//因為後面配置了路由,所以就這樣寫就行 query:{ id : this.radio }//這個是傳遞的引數,ID值 }); window.open(routeData.href, '_blank') } },
2.配置路由路徑
像這種開啟新頁面,要配置路由路徑
{ path: '/forgetpassword', name: '忘記密碼', component: () => import( /* webpackChunkName: "page" */ '@/page/login/forgetpassword'), meta: { keepAlive: true, isTab: false, isAuth: false } }, { path: '/showDiagnose', name: '診斷報告', component: () => import( /* webpackChunkName: "page" */ '@/page/showDiagnose'), meta: { keepAlive: true, isTab: false, isAuth: false } },
3.開啟的新頁面接受引數
新頁面showDiagnose,新寫一個新頁面並接受之前頁面傳遞過來的ID
新頁面:
<template>
<div class="dashboard-editor-container">//右側滾動條
<!--報告正文內容-->
//左上角的logo圖片
<img src="../../public/img/logo.png" crossorigin="anonymous" height="114" width="147" alt="" style="text-align: left">
<!--兩個下載按鈕,放在了文章前面-->
<el-row style="text-align:center;margin-left: 1200px">
//注意,el-row中的內容如果居中的話,用style="text-align:center;
<el-button type="danger" size="small" @click="downLoadWord">WORD下載</el-button>
<el-button type="danger" size="small" @click="downLoadPdf">PDF下載</el-button>
</el-row>
<h1 style="text-align: center;
font-size: 38px;
color: rgba(17,19,19,0.77);">裝置診斷報告書</h1>
<br/>
<h2 style="margin-left: 20px;
font-size: 30px;
color: rgba(17,19,19,0.77)">Observations of failure 問題描述:</h2>
<ul>
/*裝置名稱後面接的form是底下return {}裡面的form表單,但是這個form表單沒有資料,
因為後面頁面建立的時候,就會走getDetail(id)這個方法,查出來的資料,和
*/
<li style="font-size:20px; margin-bottom: 5px;">裝置SN:{{form.sn}}</li>
<li style="font-size:20px; margin-bottom: 5px;">裝置名稱:{{form.deviceName}}</li>
<li style="font-size:20px; margin-bottom: 5px;">問題描述:{{form.problemDescription}}</li>
</ul>
<h2 style="margin-left: 20px;
font-size: 30px;
color: rgba(17,19,19,0.77)">Estimation of reason原因分析:</h2>
<ul>
<li style="font-size:20px; margin-bottom: 5px;">{{form.estimationReason}}</li>
</ul>
<h2 style="margin-left: 20px;
font-size: 30px;
color: rgba(17,19,19,0.77)">Spare parts備件:</h2>
<ul>
<li style="font-size:20px; margin-bottom: 5px;">{{form.spareParts}}</li>
</ul>
<h2 style="margin-left: 20px;
font-size: 30px;
color: rgba(17,19,19,0.77)">Final result結果/反饋:</h2>
<ul>
<li style="font-size:20px; margin-bottom: 5px;">{{form.finalResult}}</li>
</ul>
<h2 style="margin-left: 20px;
font-size: 30px;
color: rgba(17,19,19,0.77)">Recommendation建議:</h2>
<ul>
<li style="font-size:20px; margin-bottom: 5px;">{{form.recommendation}}</li>
</ul>
</div>
</template>
<script>
import {getDetail, downloadWord, downloadPdf} from '../api/monitor/diagnose'
export default {
data() {
return {
htmlTitle: "裝置診斷報告書",
form: {
}
}
},
//頁面建立的時候,會走這個裡面,生命週期方法,也就是說這個頁面剛建立的時候,ID就傳過來了,
//同時也會走getDetail(id)這個方法
created() {
//用this.$route點後面的引數,接受上一個頁面傳遞過來的id
let id = this.$route.query.id
/*把ID傳到getDetail方法中,得到結果res,把查詢到的內容附到
form: {
}
這個表單中,相當於這個表單中轉一下。
*/
getDetail(id).then(res => {
this.form = res.data.data;
})
},
methods: {
//下載word的方法
downLoadWord() {
//先是把id引過來
let id = this.$route.query.id
downloadWord(id).then(res => {
//先判斷res.data有沒有這個資料,然後判斷code等不等於200,因為成功是200,失敗是400
if (res.data && res.data.code === 200) {
window.location.href = "/api" + res.data.data;
/*windows.location.href="/url" 當前頁面開啟URL頁面
詳細記錄是:https://blog.csdn.net/sdta25196/article/details/78799338
經過測試:res.data.data: /upload/1596791395547.docx,也就是說後端傳回來的就是:
/upload/1596791395547.docx這個路徑
而開啟的路徑就是:/api/upload/1596791395547.docx
在vue.config.jswe檔案中設定:
devServer: {
port: 1888,
proxy: {
'/api': {
//本地服務介面地址
target: 'http://localhost:8088',
//遠端演示服務地址,可用於直接啟動專案
//target: 'https://saber.bladex.vip/api',
ws: true,
pathRewrite: {
'^/api': '/'
}
}
}
}
*/
後臺的路徑,前臺轉換成/api
}
//
})
},
// 下載pdf,和之前下載word也是一樣的
downLoadPdf() {
let id = this.$route.query.id
downloadPdf(id).then(res => {
console.log(res)
if (res.data && res.data.code === 200) {
window.location.href = "/api" + res.data.data;
}
//
})
}
}
}
</script>
<!--右側滾動條-->
//這是網上查到的右側滾動條的程式碼
<style rel="stylesheet/scss" lang="scss" scoped>
.dashboard-editor-container {//這個是滾動條的類的設定
padding: 15px;
background-color: #ffffff;
overflow-y: auto;
height: 672px;
.chart-wrapper {
background: #ffffff;
padding: 16px 16px 0;
margin-bottom: 32px;
}
}
</style>
4.在src/api中
//get方法,引數只有一個,即id
export const downloadWord = (id) => {
return request({
url: '/api/diagnose/downloadWord',
method: 'get',
params: {id}
})
};
export const downloadPdf = (id) => {
return request({
url: '/api/diagnose/downloadPdf',
method: 'get',
params: {id}
})
};
5.vue.config.js檔案
devServer: {
port: 1888,
proxy: {
'/api': {
//本地服務介面地址
target: 'http://localhost:8088',
//遠端演示服務地址,可用於直接啟動專案
//target: 'https://saber.bladex.vip/api',
ws: true,
pathRewrite: {//反向代理
'^/api': '/'
}
}
}
}
B後端程式碼
1.在控制器層:DiagnoseController
返回的型別是String型別
而返回的是路徑
/**
* 下載報告word版本
*/
@GetMapping("/downloadWord")
public R<String> downloadWord(@RequestParam Long id) {
String path = diagnoseService.downloadWord(id);
//返回的是地址
return R.data(path);
}
/**
* 下載報告PDF版本
*/
@GetMapping("/downloadPdf")
public R<String> downloadPdf(@RequestParam Long id) {
String path = diagnoseService.downloadPdf(id);
return R.data(path);
}
點選方法,進入業務層介面
2.在業務層介面:IDiagnoseService
/**
* 下載word版診斷報告
* @param id 診斷報告id
* @return 診斷報告word檔案路徑
*/
String downloadWord(Long id);
/**
* 下載Pdf版診斷報告
* @param id 診斷報告id
* @return 診斷報告Pdf檔案路徑
*/
String downloadPdf(Long id);
點選抽象方法,進入業務層實現類
3.在業務層實現類DiagnoseServiceImpl
package com.tcb.monitor.service.impl;
/**
* 診斷報告業務層實現類
*
* @author Charles
* @since 2020/07/21
*/
@Service
@Slf4j
public class DiagnoseServiceImpl extends BaseServiceImpl<DiagnoseMapper, Diagnose> implements IDiagnoseService {
@Resource
IDepartmentService departmentService;
@Resource
DiagnoseMapper diagnoseMapper;
@Value("${upload.path}")
private String uploadPath;
//下載word 的方法
@Override
public String downloadWord(Long id) {
//用之前的查詢詳情的方法,根據id返回的是VO
DiagnoseDetailVo vo = getDiagDetail(id);
//generateWordFile為自己寫的方法,在後面
//根據VO,返回的是檔名
String fileName = generateWordFile(vo);
// 3. 將檔案路徑返回給伺服器
return "/upload/" + fileName;
}
@Override
public String downloadPdf(Long id) {
//根據id返回VO類的物件
DiagnoseDetailVo vo = getDiagDetail(id);
//這裡是因為丟擲異常
String fileName = "";
try {
fileName = generatePdfFile(vo);
} catch (Exception e) {
e.printStackTrace();
}
// 3. 將檔案路徑返回給伺服器
return "/upload/" + fileName;
}
private String generatePdfFile(DiagnoseDetailVo vo) throws DocumentException, IOException, FontFormatException {
// 1. 生成pdf文件
/*https://www.runoob.com/java/java-bytearrayoutputstream.html
位元組陣列輸出流在記憶體中建立一個位元組陣列緩衝區,所有傳送到輸出流的資料儲存在該位元組陣列緩衝區中。建立位元組陣列輸出流物件有以下幾種方式。下面的構造方法建立一個32位元組(預設大小)的緩衝區。
OutputStream bOut = new ByteArrayOutputStream();*/
//位元組陣列輸出流,輸出流名稱:stream
ByteArrayOutputStream stream = new ByteArrayOutputStream();
//此處根據excel大小設定pdf紙張大小
Document document = new Document(PageSize.A4);
//將PDF文件物件寫入到流
PdfWriter.getInstance(document, stream);
//設定頁邊距
document.setMargins(30, 30, 15, 15);
/*
Write物件建立之後
首先開啟documet(這個過程就像我們建立一個空的pdf檔案,然後開啟來創作一樣)
然後開始寫入資料
設定文件屬性
最後關閉
*/
document.open();
//設定基本字型
URL resource = DiagnoseServiceImpl.class.getClassLoader().getResource("font/simsun.ttc");
//字尾是.ttc的都是字型檔案
//simsun.ttc:這個字型是宋體,ttc字尾的文bai件一般是一套字型,多數就是du包含了一種字型的正常zhi體,黑體,和斜體。
BaseFont baseFont = BaseFont.createFont(Objects.requireNonNull(resource).toString() + ",0", BaseFont.IDENTITY_H,BaseFont.EMBEDDED);
PdfPTable table = new PdfPTable(1);
table.setWidthPercentage(20f);
table.setHorizontalAlignment(Element.ALIGN_LEFT);
InputStream logo = DiagnoseServiceImpl.class.getClassLoader().getResourceAsStream("logo.png");
BufferedImage image = null;
if (logo != null) {
image = ImageIO.read(logo);
ByteArrayOutputStream out = new ByteArrayOutputStream();
ImageIO.write(image, "png", out);
byte[] data = out.toByteArray();
PdfPCell cell = new PdfPCell(Image.getInstance(data), true);
cell.setBorder(0);
table.addCell(cell);
document.add(table);
}
// 標題
Font pdFont = new Font(baseFont, 20, Font.BOLD, BaseColor.BLACK);
Paragraph p = new Paragraph("裝置診斷報告書", pdFont);
p.setAlignment(Element.ALIGN_CENTER);
document.add(p);
// 問題描述
pdFont = new Font(baseFont, 18, Font.BOLD, BaseColor.BLACK);
p = new Paragraph("Observations of failure 問題描述:", pdFont);
p.setAlignment(Element.ALIGN_LEFT);
document.add(p);
pdFont = new Font(baseFont, 14, Font.NORMAL, BaseColor.BLACK);
p = new Paragraph("裝置SN:" + vo.getSn(), pdFont);
p.setAlignment(Element.ALIGN_LEFT);
document.add(p);
pdFont = new Font(baseFont, 14, Font.NORMAL, BaseColor.BLACK);
p = new Paragraph("裝置名稱:" + vo.getDeviceName(), pdFont);
p.setAlignment(Element.ALIGN_LEFT);
document.add(p);
pdFont = new Font(baseFont, 14, Font.NORMAL, BaseColor.BLACK);
p = new Paragraph("問題描述:" + vo.getProblemDescription(), pdFont);
p.setAlignment(Element.ALIGN_LEFT);
document.add(p);
// 原因分析
pdFont = new Font(baseFont, 18, Font.BOLD, BaseColor.BLACK);
p = new Paragraph("Estimation of reason原因分析:", pdFont);
p.setAlignment(Element.ALIGN_LEFT);
document.add(p);
pdFont = new Font(baseFont, 14, Font.NORMAL, BaseColor.BLACK);
p = new Paragraph(vo.getEstimationReason(), pdFont);
p.setAlignment(Element.ALIGN_LEFT);
document.add(p);
// 備件
pdFont = new Font(baseFont, 18, Font.BOLD, BaseColor.BLACK);
p = new Paragraph("Spare parts備件:", pdFont);
p.setAlignment(Element.ALIGN_LEFT);
document.add(p);
pdFont = new Font(baseFont, 14, Font.NORMAL, BaseColor.BLACK);
p = new Paragraph(vo.getSpareParts(), pdFont);
p.setAlignment(Element.ALIGN_LEFT);
document.add(p);
// 結果/反饋
pdFont = new Font(baseFont, 18, Font.BOLD, BaseColor.BLACK);
p = new Paragraph("Final result結果/反饋:", pdFont);
p.setAlignment(Element.ALIGN_LEFT);
document.add(p);
pdFont = new Font(baseFont, 14, Font.NORMAL, BaseColor.BLACK);
p = new Paragraph(vo.getFinalResult(), pdFont);
p.setAlignment(Element.ALIGN_LEFT);
document.add(p);
// 建議
pdFont = new Font(baseFont, 18, Font.BOLD, BaseColor.BLACK);
p = new Paragraph("Recommendation建議:", pdFont);
p.setAlignment(Element.ALIGN_LEFT);
document.add(p);
pdFont = new Font(baseFont, 14, Font.NORMAL, BaseColor.BLACK);
p = new Paragraph(vo.getRecommendation(), pdFont);
p.setAlignment(Element.ALIGN_LEFT);
document.add(p);
document.add(p);
document.newPage();
document.close();
// 2. 將文件輸出到本地
String fileName = System.currentTimeMillis() + ".pdf";
String filePath = uploadPath + fileName;
writeToFile(filePath, stream);
return fileName;
}
private static void writeToFile(String pdfPath, ByteArrayOutputStream stream) throws IOException {
byte[] pdfByte = stream.toByteArray();
stream.flush();
stream.reset();
stream.close();
FileOutputStream outputStream = new FileOutputStream(pdfPath);
outputStream.write(pdfByte);
outputStream.flush();
outputStream.close();
}
private String generateWordFile(DiagnoseDetailVo vo) {
// 1. 根據模板生成word文件
// logo
//建立word檔案,document是“檔案”的英文。建立Pdf檔案的是,括號內就要設定PageSize.A4
//而這裡如果括號內加入PageSize.A4,則報錯
XWPFDocument document = new XWPFDocument();
//新建一個段落p
XWPFParagraph p = document.createParagraph();
// 設定段落的對齊方式,ParagraphAlignment,中文意思是段落對其,是列舉類,在後續有記錄
//此處為左側對齊
p.setAlignment(ParagraphAlignment.LEFT);
//建立段落文字
XWPFRun run = p.createRun();
//位元組輸入流InputStream
/*
class是指當前類的class物件.
getClassLoader()是獲取當前的類載入器,什麼是類載入器?簡單點說,就是用來載入java類的,類載入器負責把class檔案載入進記憶體中,並建立一個java.lang.Class類的一個例項,也就是class物件,並且每個類的類載入器都不相同。
getResourceAsStream(path)是用來獲取資源的,而類載入器預設是從classPath下獲取資源的,因為這下面有class檔案,所以這段程式碼總的意思是通過類載入器在classPath目錄下獲取資源.並且是以流的形式。
*/
//logo
try (InputStream logo = DiagnoseServiceImpl.class.getClassLoader().getResourceAsStream("logo.png")) {
//Class.getClassLoader.getResourceAsStream(String path) :預設則是從ClassPath根下獲取,path不能以’/'開頭,因為是相對路徑,是相對的,如果前面加/則是絕對路徑,最終是由ClassLoader獲取資源。
//說明classpath和resources是一個位置
//原始碼:public XWPFPicture addPicture(InputStream pictureData, int pictureType, String filename, int width, int height),引數有 輸入流、圖片型別、檔名、寬度、高度
run.addPicture(logo, org.apache.poi.xwpf.usermodel.Document.PICTURE_TYPE_PNG, "a.png", Units.toEMU(147), Units.toEMU(114));
} catch (InvalidFormatException | IOException e) {
e.printStackTrace();
}
// 標題
// 新建一個段落
p = document.createParagraph();
//這個是居中對齊
p.setAlignment(ParagraphAlignment.CENTER);
generateRun(p, "裝置診斷報告書", true, 20);
/*generateRun是自己生產的一個方法,把重複的幾個方法放在了一個方法裡,如下:
private XWPFRun generateRun(XWPFParagraph p, String text, boolean bold, int fontSize) {
這個方法中,
XWPFRun run = p.createRun();//為建立段落文字
run.setText(text);//為輸入 word中的文字
run.setBold(bold);//設定為粗體,bold是boolean屬性
run.setFontSize(fontSize);//應該是設定字號,即設定字型大小,font即字型的意思
run.setFontFamily("微軟雅黑");//設定字型樣式
return run;
}
*/
// 問題描述
p = document.createParagraph();
p.setAlignment(ParagraphAlignment.LEFT);
generateRun(p, "Observations of failure 問題描述:", true, 18);
p = document.createParagraph();
p.setAlignment(ParagraphAlignment.LEFT);
generateRun(p, "裝置SN:" + vo.getSn(), false, 14);
p = document.createParagraph();
p.setAlignment(ParagraphAlignment.LEFT);
generateRun(p, "裝置名稱:" + vo.getDeviceName(), false, 14);
p = document.createParagraph();
p.setAlignment(ParagraphAlignment.LEFT);
generateRun(p, "問題描述:" + vo.getProblemDescription(), false, 14);
// 原因分析
p = document.createParagraph();
p.setAlignment(ParagraphAlignment.LEFT);
generateRun(p, "Estimation of reason原因分析:", true, 18);
p = document.createParagraph();
p.setAlignment(ParagraphAlignment.LEFT);
generateRun(p, vo.getEstimationReason(), false, 14);
// 備件
p = document.createParagraph();
p.setAlignment(ParagraphAlignment.LEFT);
generateRun(p, "Spare parts備件:", true, 18);
p = document.createParagraph();
p.setAlignment(ParagraphAlignment.LEFT);
generateRun(p, vo.getSpareParts(), false, 14);
// 結果/反饋
p = document.createParagraph();
p.setAlignment(ParagraphAlignment.LEFT);
generateRun(p, "Final result結果/反饋:", true, 18);
p = document.createParagraph();
p.setAlignment(ParagraphAlignment.LEFT);
generateRun(p, vo.getFinalResult(), false, 14);
// 建議
p = document.createParagraph();
p.setAlignment(ParagraphAlignment.LEFT);
generateRun(p, "Recommendation建議:", true, 18);
p = document.createParagraph();
p.setAlignment(ParagraphAlignment.LEFT);
generateRun(p, vo.getRecommendation(), false, 14);
// 2. 將word輸出到本地目錄
String fileName = System.currentTimeMillis() + ".docx";
//檔名 = 當前系統時間+docx字尾
//檔案地址 =uploadPath +filename
/*uploadPath就是前面的:
@Value("${upload.path}")
private String uploadPath;
應該是對應的配置檔案的 path: E:/upload/。
所以檔案地址 = E:/upload/+時間+docx字尾*/
String filePath = uploadPath + fileName;//這個是電腦上地址
//根據電腦上的檔案地址建立檔案
File file = new File(filePath);
try {
//建立檔案
document.write(new FileOutputStream(file));
} catch (IOException e) {
e.printStackTrace();
}
//返回檔名
return fileName;
}
//自己生成的一個方法,因為只是這個類用,所以許可權為private
private XWPFRun generateRun(XWPFParagraph p, String text, boolean bold, int fontSize) {
XWPFRun run = p.createRun();
run.setText(text);
run.setBold(bold);
run.setFontSize(fontSize);
run.setFontFamily("微軟雅黑");
return run;
}
}
在網上的建立PDF筆記
https://cloud.tencent.com/developer/article/1636609
建立Document物件,三種方式:
Document document =new Document(); // 預設頁面大小是A4
Document document =new Document(PageSize.A4); // 指定頁面大小為A4
Document document =new Document(PageSize.A4,50,50,30,20); // 指定頁面大小為A4,且自定義頁邊距(marginLeft、marginRight、marginTop、marginBottom)
4.application.yml:
在配置中,有(application:應用)
upload:
path: E:/upload/
5.BladeConfiguration檔案
@Value("${upload.path}")
private String uploadPath;
附:
ParagraphAlignment:段落對齊列舉類
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.apache.poi.xwpf.usermodel;
import java.util.HashMap;
import java.util.Map;
public enum ParagraphAlignment {
LEFT(1),
CENTER(2),
RIGHT(3),
BOTH(4),
MEDIUM_KASHIDA(5),
DISTRIBUTE(6),
NUM_TAB(7),
HIGH_KASHIDA(8),
LOW_KASHIDA(9),
THAI_DISTRIBUTE(10);
private static Map<Integer, ParagraphAlignment> imap = new HashMap();
private final int value;
private ParagraphAlignment(int val) {
this.value = val;
}
public static ParagraphAlignment valueOf(int type) {
ParagraphAlignment err = (ParagraphAlignment)imap.get(type);
if (err == null) {
throw new IllegalArgumentException("Unknown paragraph alignment: " + type);
} else {
return err;
}
}
public int getValue() {
return this.value;
}
static {
ParagraphAlignment[] arr$ = values();
int len$ = arr$.length;
for(int i$ = 0; i$ < len$; ++i$) {
ParagraphAlignment p = arr$[i$];
imap.put(p.getValue(), p);
}
}
}