springboot导出pdf(加水印)(静态资源文件夹配置及使用)

加入itext依赖:

<!--用于导出pdf-->
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itextpdf</artifactId>
            <version>5.5.13</version>
        </dependency>
package com.ruoyi.review.controller;

import com.itextpdf.text.*;
import com.itextpdf.text.Font;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.*;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.review.dto.AppraiseExportDataDto;
import com.ruoyi.review.dto.GradeResultDto;
import org.apache.poi.ss.usermodel.Cell;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;

import static com.itextpdf.text.pdf.GrayColor.GRAYBLACK;

/**
 * @author :xs
 * @date : 2023/3/22
 * @description :
 */
@RestController
@RequestMapping("/exportPdf")
public class ExportPdfController {



    @Value("${spring.watermark}")
    private String springWatermark;

//    需要把“查看评分”的数据传入 GradeResultDtoList
    @GetMapping("/generate-pdf")
    public @ResponseBody void generatePdf(HttpServletResponse response,@RequestBody List<GradeResultDto> GradeResultDtoList) throws Exception {

        // 设置响应头   可以通过在响应体中写入PDF文件的数据,然后将该响应的Content-Disposition标头设置为attachment来将其作为附件下载到客户端
        response.setContentType("application/pdf");
        response.setHeader("Content-Disposition", "attachment; filename=\"export.pdf\"");

        // 获取输出流
        OutputStream outputStream = response.getOutputStream();

        // 创建文档
        Document document = new Document(PageSize.A4);
        //  在iText库中,PdfWriter类用于将PDF文件写入输出流或文件中。第一个参数是一个Document对象,用于表示要写入PDF文件的内容;第二个参数是一个OutputStream对象,用于表示PDF文件的输出目标,可以是一个文件输出流、内存输出流等
        //  PdfWriter.getInstance(document, outputStream);

       /*
        创建一个内存输出流,用于将PDF文件的内容输出到内存中(不用上边的文本流,用内存存储读取方式,是因为添加水印时,需要将PDF文件的内容读取到内存中,获取总共多少页,再加入)
        注意,这种方式会将整个PDF文件的内容都读取到内存中,然后再写入到输出流中,可能会导致内存溢出或性能问题。因此,在处理大文件时,最好使用基于流的方式逐个页面地读取和写入PDF文件的内容。
       */
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        // 创建一个PdfWriter对象,用于将PDF文件的内容输出到内存输出流中
        PdfWriter writer = PdfWriter.getInstance(document, baos);
        document.open();

        // 创建表格   加表头(4列的表格)
        PdfPTable table = new PdfPTable(4);
        //设置表格宽度占页面宽度的百分比
       table.setWidthPercentage(100);
        // 设置文本方向为从左到右
        table.setRunDirection(PdfWriter.RUN_DIRECTION_LTR);

        // 在表格中添加单元格并设置字体
        table.addCell(createCell("教师编号"));
        table.addCell(createCell("教师名字"));
        table.addCell(createCell("总分"));
        table.addCell(createCell("是否推荐"));
        for(GradeResultDto appraiseExportDataDto:GradeResultDtoList){
//        把数据传到appraiseExportDataDto中,先弄一下测试数据
     /*       appraiseExportDataDto.setTeacherId(1L);
            appraiseExportDataDto.setTeacherName("sss");
            appraiseExportDataDto.setScoresResult(BigDecimal.valueOf(90.222));
            appraiseExportDataDto.setIsRecommend(true);*/

            table.addCell(createCell(String.valueOf(appraiseExportDataDto.getTeacherId())));
            table.addCell(createCell(String.valueOf(appraiseExportDataDto.getTeacherName())));
            table.addCell(createCell(String.valueOf(appraiseExportDataDto.getScoresResult())));
//            table.addCell(createCell(String.valueOf(appraiseExportDataDto.getIsRecommend())));
            table.addCell(createCell(booleanToChinese(appraiseExportDataDto.getIsRecommend())));
        }

        // 将表格添加到文档中
        document.add(table);
        // 关闭文档
        document.close();

        writer.close();


        String watermark =springWatermark;
        try {
            // 读取 PDF 文件并创建 PdfReader 对象
            PdfReader reader = new PdfReader(baos.toByteArray());

            // 创建一个文本水印
            BaseFont font1 = BaseFont.createFont("ruoyi-admin/src/main/java/com/ruoyi/review/utils/fangsong.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
           /* Font.NORMAL是iText中的一种字体样式,表示正常字体样式,也称为普通样式。它是iText的Font类中的四种字体样式之一,
           其他样式包括Font.BOLD(加粗样式)、Font.ITALIC(斜体样式)和Font.UNDERLINE(下划线样式)。
            使用Font.NORMAL设置字体样式时,不会应用任何加粗、倾斜或下划线效果,文本将显示为普通的正常字体样式。*/
            Font font = new Font(font1, 30, Font.NORMAL);
            Phrase phrase = new Phrase(watermark, font);
            PdfContentByte canvas;
            PdfGState gs = new PdfGState();
            gs.setFillOpacity(0.1f);
            gs.setStrokeOpacity(0.1f);

            // 循环遍历每一页 PDF,向其添加水印
            int totalPages = reader.getNumberOfPages(); // 获取PDF文件的总页数
            PdfStamper stamper = new PdfStamper(reader, response.getOutputStream()); // 创建PdfStamper对象,用于在PDF文件上添加内容
            for (int i = 1; i <= totalPages; i++) { // 遍历PDF文件的每一页
                canvas = new PdfContentByte(stamper.getWriter()); // 创建PdfContentByte对象,用于在PDF文件上绘制内容
                canvas = stamper.getUnderContent(i); // 获取当前页的PdfContentByte对象,并将内容添加到其下层
                canvas.beginText(); // 开始绘制文本
                canvas.setGState(gs); // 设置透明度
                BaseColor watermarkColor = new BaseColor(0, 0, 0);
               /* //上边的Font font = new Font(font1, 40, Font.NORMAL);覆盖了这里,所以字体大小和样式都没有起作用
                canvas.setColorFill(watermarkColor); // 设置文本颜色   要在设置透明度之后
                System.out.println(font+"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
                canvas.setFontAndSize(font.getBaseFont(), 25); // 设置字体和字号 */
                Rectangle rect = reader.getPageSizeWithRotation(i); // 获取当前页的大小
                ColumnText.showTextAligned(canvas, Element.ALIGN_CENTER, phrase, rect.getWidth() / 2, rect.getHeight() / 2, 45); // 在PDF文件的当前页上居中绘制文本
                canvas.endText(); // 结束绘制文本
            }
            // 关闭 PdfStamper 和 PdfReader 对象
            stamper.close();
            reader.close();
        } catch (Exception e) {
            throw new Exception("添加水印时出现异常:" + e.getMessage());
        }



        // 将内存输出流中的数据写入文件输出流中
        outputStream.write(baos.toByteArray());

        // 刷新和关闭输出流
        outputStream.flush();
        outputStream.close();

    }

    /*
       iText 5 中默认支持了多种中文字体,包括:
       STSong-Light:华文宋体
       STHeiti-Light:华文黑体
       STKaiti-Light:华文楷体
       STZhongsong:华文中宋
       FangSong:仿宋
   */
    // 创建带有四个表头的单元格
    private PdfPCell createCell(String text) throws DocumentException, IOException {
        //  因为用设置默认的单元格   中文字体会爆红table.getDefaultCell().setFont(font);   没有setFont
        // 所以中文没办法显示的解决办法:可以引入字体来显示  创建支持中文字符集的字体对象   BaseFont.createFont()方法来加载它们
        BaseFont bfChinese =BaseFont.createFont("ruoyi-admin/src/main/java/com/ruoyi/review/utils/fangsong.ttf", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
        Font fontChinese = new Font(bfChinese, 12, Font.NORMAL);
        PdfPCell cell=new PdfPCell(new Phrase(text, fontChinese));

        // 设置单元格的垂直对齐方式为居中
        cell.setVerticalAlignment(com.itextpdf.text.Element.ALIGN_MIDDLE);
        cell.setHorizontalAlignment(com.itextpdf.text.Element.ALIGN_CENTER);

        //设置单元格是否使用上行和下行
        cell.setUseAscender(true);
        cell.setUseDescender(true);

        //用于设置单元格的内边距
        cell.setPadding(5);

        // 设置单元格跨两列 (就是合并两个单元格)
        // cell.setColspan(2);
        // cell.setRowspan(2);

        // 设置单元格的背景颜色
        // cell.setBackgroundColor(BaseColor.LIGHT_GRAY);

        // 设置单元格的边框宽度和颜色
        cell.setBorderWidth(1);
        cell.setBorderColor(BaseColor.BLACK);

        return cell;
    }

    //把布尔类型的转换成中文
    public static String booleanToChinese(boolean value) {
        return value ? "是" : "否";
    }



}

但是上述代码如果部署到服务器上之后,会出现字体文件找不到的问题
“ruoyi-admin/src/main/java/com/ruoyi/review/utils/fangsong.ttf”(没有那个文件或目录)

原因:

如果文件不放在静态文件夹下面,且没有在 Spring Boot 项目中单独定义其他路径,那么在部署时通常不会被访问到。因为 Spring Boot 默认只会映射静态文件夹下的文件,如果要访问其他路径下的文件,需要在 Spring Boot 项目中进行配置。

解决办法:

1、把字体文件放在静态文件夹resources/fonts下
2、配置yml文件

spring:
  itext:
    fonts:
      dir: classpath:/fonts

3、更改字体路径代码(两处,一个是表格中的文字,一个是水印中的文字)

BaseFont font1 = BaseFont.createFont("fonts/fangsong.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);

你可能感兴趣的:(java,servlet,spring)