vue+java创建word和pdf文档(XWPFDocument)

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);
        }

    }
}
posted @ 2022-02-16 10:55  Charles博客  阅读(632)  评论(0编辑  收藏  举报