java文件上传时给pdf、word、excel、ppt、图片添加水印

前言

在开发的过程中,因为文件的特殊性,需要给pdf、word、excel、ppt、图片添加水印。添加水印可以在文件上传时添加,也可以在文件下载时添加。因为业务的某些原因,文件需要在浏览器预览,如果用户将文件另存为则无法添加水印,所以此文章主要介绍文件上传时添加水印。至于文件下载时添加水印功能也很简单,稍微修改即可。

一:文件上传

1.1 controller控制层

@ApiOperation("上传文件")
    @PostMapping("/file/upload")
    public String uploadFile(@RequestParam(value = "file") MultipartFile file) {
        if (file == null || file.isEmpty()) {
            throw new BusinessException("上传文件为空");
        }
        String originalFilename = file.getOriginalFilename();
        if (StringUtils.isBlank(originalFilename)) {
            throw new BusinessException("上传文件名为空");
        }
        String filePath = obsClientHelper.upload(file, file.getOriginalFilename());
        return filePath;
    }

1.2 文件上传到服务器


    /**
     * 上传文件到服务器
     *
     * @param uploadFile 文件
     * @param fileName   文件名称
     * @return 文件路径
     */
    @SneakyThrows
    public String upload(MultipartFile uploadFile, String fileName) {
        String objectKey = directory + "/" + DATE_TIME_FORMATTER.format(LocalDate.now()) + "/" + UUID.randomUUID() + "/" + fileName;
        InputStream inputStream = null;
        //上传文件添加水印
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String watermark = UserContext.currentUser().getUserName() + "(" + UserContext.currentUser().getUserId() + ")" + "\n" + simpleDateFormat.format(new Date());
        if (fileName.endsWith("pdf")) {
            inputStream = PdfWatermarkUtils.addWatermarkInputStream(uploadFile.getInputStream(), new PdfWatermarkPageEventHelper(watermark));
        } else if (fileName.endsWith(ImageConstants.PICTURE_JPG) || fileName.endsWith(ImageConstants.PICTURE_PNG)) {
            inputStream = ImageWatermarkUtil.imgWatermarkInputStream(watermark, uploadFile.getInputStream(), -40);
        } else if (fileName.endsWith("docx") || fileName.endsWith("xlsx") || fileName.endsWith("pptx")) {
            TextWaterMarkDTO waterMarkDTO = new TextWaterMarkDTO(fileName, watermark);
            inputStream = OfficeWatermarkUtil.doMarkInputStream(uploadFile.getInputStream(), waterMarkDTO);
        } else {
            inputStream = uploadFile.getInputStream();
        }
        if (inputStream == null) {
            throw new RuntimeException("上传文件时,文件添加水印失败");
        }
        try (ObsClient obsClient = new ObsClient(ak, sk, endPoint)) {
            PutObjectResult putObjectResult = obsClient.putObject(bucketName, objectKey, inputStream);
            if (putObjectResult.getStatusCode() != HttpServletResponse.SC_OK) {
                throw new RuntimeException("文件上传失败,OBS响应码:" + putObjectResult.getStatusCode());
            }
            return putObjectResult.getObjectKey();
        } catch (IOException e) {
            throw new RuntimeException("文件上传失败", e);
        }
    }

二:PDF添加水印

2.1 引入依赖

<dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>kernel</artifactId>
            <version>7.1.11</version>
        </dependency>

        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>layout</artifactId>
            <version>7.1.11</version>
        </dependency>

        <!--没有该包的话,会有中文显示问题-->
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itextpdf</artifactId>
            <version>5.4.3</version>
        </dependency>

        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itext-asian</artifactId>
            <version>5.2.0</version>
        </dependency>

        <dependency>
            <groupId>com.itextpdf.tool</groupId>
            <artifactId>xmlworker</artifactId>
            <version>5.5.11</version>
        </dependency>

2.2 PdfWatermarkUtils


    /**
     * pdf加水印
     * @param inputStream 输入流
     * @param pdfPageEventHelper 水印
     * @return InputStream
     * @throws IOException
     */
    public static InputStream addWatermarkInputStream(InputStream inputStream, PdfPageEventHelper pdfPageEventHelper) throws IOException {
        String watermarkText = ((PdfWatermarkPageEventHelper) pdfPageEventHelper).getWatermarkText();
        float fontSize = 13;
        int rowSpace = 150;
        int colSpace = 150;
        boolean linux = SystemUtil.getOsInfo().isLinux();
        String chineseFontPath = null;
        if (linux) {
            chineseFontPath = "/usr/share/fonts/STSONG.TTF";
        } else {
            chineseFontPath = SystemUtil.getUserInfo().getCurrentDir() + "\\fonts\\STSONG.TTF";
        }
        watermarkText = watermarkText.replace("\n", "  ");
        // 加载PDF文件
        PDDocument document = PDDocument.load(inputStream);
        document.setAllSecurityToBeRemoved(true);
        // 加载水印字体
        PDFont font = PDType0Font.load(document, new FileInputStream(chineseFontPath), true);
        // 遍历PDF文件,在每一页加上水印
        for (PDPage page : document.getPages()) {
            PDPageContentStream stream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, true, true);
            PDExtendedGraphicsState r = new PDExtendedGraphicsState();
            // 设置透明度
            r.setNonStrokingAlphaConstant(0.2f);
            r.setAlphaSourceFlag(true);
            stream.setGraphicsStateParameters(r);
            // 设置水印字体颜色
            stream.setStrokingColor(Color.GRAY);
            stream.beginText();
            stream.setFont(font, fontSize);
            stream.newLineAtOffset(0, -15);
            // 获取PDF页面大小
            float pageHeight = page.getMediaBox().getHeight();
            float pageWidth = page.getMediaBox().getWidth();
            // 根据纸张大小添加水印,30度倾斜
            for (int h = 10; h < pageHeight; h = h + rowSpace) {
                for (int w = -10; w < pageWidth; w = w + colSpace) {
                    stream.setTextMatrix(Matrix.getRotateInstance(0.3, w, h));
                    stream.showText(watermarkText);
                }
            }
            // 结束渲染,关闭流
            stream.endText();
            stream.restoreGraphicsState();
            stream.close();
        }
        return pdDocumentConvertorStream(document);
    }

    /**
     * 将PDDocument转化为InputStream
     *
     * @param document document
     * @return InputStream
     */
    public static InputStream pdDocumentConvertorStream(PDDocument document) {
        try {
            //临时缓冲区
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            document.save(out);
            document.close();
            return new ByteArrayInputStream(out.toByteArray());
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return null;
        }
    }

三:图片添加水印

3.1 ImageWatermarkUtil

import com.itextpdf.text.Element;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.pdf.ColumnText;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.Objects;

@Component
@Slf4j
public class ImageWatermarkUtil {
    /**
     * 水印透明度
     */
    private static final float ALPHA = 0.5f;
    /**
     * 水印文字大小
     */
    public static final int FONT_SIZE = 23;
    /**
     * 水印文字字体
     */
    private static Font FONT;
    /**
     * 水印文字颜色
     */
    private static final Color COLOR = Color.white;
    /**
     * 水印之间的间隔
     */
    private static final int X_MOVE = 60;
    /**
     * 水印之间的间隔
     */
    private static final int Y_MOVE = 60;

    /**
     * 获取文本长度。汉字为1:1,英文和数字为2:1
     */
    private static int getTextLength(String text) {
        int length = text.length();
        for (int i = 0; i < text.length(); i++) {
            String s = String.valueOf(text.charAt(i));
            if (s.getBytes().length > 1) {
                length++;
            }
        }
        length = length % 2 == 0 ? length / 2 : length / 2 + 1;
        return length;
    }

    /**
     * 图片添加水印
     *
     * @param logoText         水印内容
     * @param sourceFileStream 流文件
     * @param degree           倾斜角度
     * @return InputStream
     */
    public static InputStream imgWatermarkInputStream(String logoText, InputStream sourceFileStream, Integer degree) {
        try {
            BufferedImage bufferedImage = addWatermark(logoText, sourceFileStream, degree);
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            ImageIO.write(bufferedImage, "jpg", os);
            return new ByteArrayInputStream(os.toByteArray());
        } catch (IOException e) {
            return null;
        }
    }


    /**
     * 图片添加水印
     *
     * @param logoText         水印内容
     * @param sourceFileStream 流文件
     * @param degree           倾斜角度
     * @return BufferedImage
     */
    public static BufferedImage addWatermark(String logoText, InputStream sourceFileStream, Integer degree) throws IOException {
        //源图片
        Image srcImg = ImageIO.read(sourceFileStream);
        //原图宽度
        int width = srcImg.getWidth(null);
        //原图高度
        int height = srcImg.getHeight(null);
        BufferedImage buffImg = new BufferedImage(srcImg.getWidth(null), srcImg.getHeight(null),
                BufferedImage.TYPE_INT_RGB);
        // 得到画笔对象
        Graphics2D g = buffImg.createGraphics();
        // 设置对线段的锯齿状边缘处理
        g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        g.drawImage(srcImg.getScaledInstance(srcImg.getWidth(null), srcImg.getHeight(null), Image.SCALE_SMOOTH),
                0, 0, null);
        // 设置水印旋转
        if (null != degree) {
            g.rotate(Math.toRadians(degree), (double) buffImg.getWidth() / 2, (double) buffImg.getHeight() / 2);
        }
        // 设置水印文字颜色
        g.setColor(COLOR);
        // 设置水印文字Font
        g.setFont(FONT);
        // 设置水印文字透明度
        g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, ALPHA));

        int x = -width / 2;
        int y = -height / 2;
        // 字体长度
        int markWidth = FONT_SIZE * getTextLength(logoText);
        // 字体高度

        // 循环添加水印
        while (x < width * 1.5) {
            y = -height;
            while (y < height * 1.5) {
                String[] lines = logoText.split("\n");
                for (String line : lines) {
                    g.drawString(line, x, y);
                    y += FONT_SIZE + Y_MOVE;
                }
            }
            x += markWidth + X_MOVE;
        }
        // 释放资源
        g.dispose();
        return buffImg;
    }


    /**
     * 给图片添加水印文字、可设置水印文字的旋转角度
     *
     * @param logoText         水印内容
     * @param sourceFileStream 输入流文件
     * @param targetFileStream 输出流文件
     * @param degree           倾斜角度
     */
    public static void imageByText(String logoText, InputStream sourceFileStream, OutputStream targetFileStream, Integer degree) {
        if (Objects.isNull(FONT)) {
            FONT = new Font(FontUtil.firstZhSupportedFontName(), Font.BOLD, FONT_SIZE);
        }
        try {
            // 生成图片
            ImageIO.write(addWatermark(logoText, sourceFileStream, degree), "JPG", targetFileStream);
            log.info("图片-添加水印文字成功!");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != targetFileStream) {
                    targetFileStream.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

}

3.2 FontUtil

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.awt.*;
import java.util.Arrays;

@Slf4j
public class FontUtil {

    private static String ZH_SUP;

    static {
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        char randomZh = '唐';
        Font[] allFonts = ge.getAllFonts();
        for (Font font : allFonts) {
            if(font.canDisplay(randomZh)){
                ZH_SUP = font.getFontName();
                break;
            }
        }
        if(StringUtils.isEmpty(ZH_SUP)){
            log.error("Zh supported font not found");
            ZH_SUP = allFonts[0].getFontName();
        }
    }

    public static String firstZhSupportedFontName(){
        return ZH_SUP;
    }

    public static java.util.List<String> getAllFontNames(){
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        String[] availableFontFamilyNames = ge.getAvailableFontFamilyNames();
        return Arrays.asList(availableFontFamilyNames);
    }
}

四:docx、xlsx、pptx添加水印

注:ppt、xls、doc由于版本新旧问题,此处先不介绍添加水印方法

4.1 TextWaterMarkDTO

import com.FontUtil;
import lombok.Data;
import lombok.experimental.Accessors;

@Data
@Accessors(chain = true)
public class TextWaterMarkDTO{

    public TextWaterMarkDTO() {
        this.fontName = FontUtil.firstZhSupportedFontName();
    }
    public TextWaterMarkDTO(String fileName,String text) {
        this.fontName = FontUtil.firstZhSupportedFontName();
        this.fileSuffix = loadFileSuffix(fileName);
        this.text = text;
    }
    String fileSuffix;
    private String text;
    private String fontName;
    private String fontColor;
    private Integer fontSize = 20;
    private Integer rotation = -20;
    Integer intervalHorizontal;
    Integer intervalVertical;
    Integer picWidth;
    Integer picHeight;
    Float alpha;
    boolean enable;

    private String loadFileSuffix(String fileName){
        String realName = fileName.trim();
        int index = realName.lastIndexOf(".");
        if( index < 0){
            return null;
        }
        return fileName.substring(index + 1);
    }

}

4.2 doMarkInputStream


    /**
     * 添加文件水印,返回InputStream
     *
     * @param input         输入流
     * @param waterMarkInfo waterMarkInfo
     * @return InputStream
     */
    public static InputStream doMarkInputStream(InputStream input, TextWaterMarkDTO waterMarkInfo) {
        InputStream inputStream = null;
        try {
            String fileSuffix = waterMarkInfo.getFileSuffix().toLowerCase();
            switch (fileSuffix) {
                case "docx":
                    inputStream = docxWaterMarkInputStream(input, waterMarkInfo);
                    break;
                case "xlsx":
                    log.info("xlsx");
                    inputStream = xlsxWaterMarkInputStream(input, waterMarkInfo.setPicWidth(1000).setPicHeight(2000));
                    break;
                case "pptx":
                    log.info("pptx");
                    inputStream = pptxAddWatermarkInputStream(input, waterMarkInfo);
                    break;
                default:
                    log.info("in default. fileSuffix:{}", fileSuffix);
                    inputStream = input;
            }
            return inputStream == null ? input : inputStream;
        } catch (Exception e) {
            log.error(ExceptionUtil.stacktraceToString(e));
            return null;
        } finally {
            close(input);
        }


    }

4.3 docx

@SneakyThrows
    private static InputStream docxWaterMarkInputStream(InputStream input, TextWaterMarkDTO waterMarkInfo) {
        XWPFDocument docx = new XWPFDocument(input);
        //设置默认值
        String watermark = StringUtils.isBlank(waterMarkInfo.getText()) ? DEFAULT_WATERMARK : waterMarkInfo.getText();
        String color = StringUtils.isBlank(waterMarkInfo.getFontColor()) ? DEFAULT_FONT_COLOR : "#" + waterMarkInfo.getFontColor();
        String fontSize = (null == waterMarkInfo.getFontSize()) ? FONT_SIZE : waterMarkInfo.getFontSize() + "pt";
        String rotation = (null == waterMarkInfo.getRotation()) ? STYLE_ROTATION : String.valueOf(waterMarkInfo.getRotation());
        DocxUtil.makeFullWaterMarkByWordArt(docx, watermark, color, fontSize, rotation);
        //将XWPFDocument转化为InputStream
        return convertToInputStream(docx);
    }

    /**
     * 将XWPFDocument转化为InputStream
     *
     * @param doc XWPFDocument
     * @return InputStream
     */
    public static InputStream convertToInputStream(XWPFDocument doc) {
        try {
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            doc.write(outputStream);
            return new ByteArrayInputStream(outputStream.toByteArray());
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            return null;
        }
    }

4.4 xlsx

@SneakyThrows
    private static InputStream xlsxWaterMarkInputStream(InputStream input, TextWaterMarkDTO waterMark) {
        XSSFWorkbook workbook = new XSSFWorkbook(input);
        Iterator<Sheet> iterator = workbook.sheetIterator();
        BufferedImage image = FontImage.createWatermarkImageFillWithText(waterMark);
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        ImageIO.write(image, "png", os);
        int pictureIdx = workbook.addPicture(os.toByteArray(), Workbook.PICTURE_TYPE_PNG);
        while (iterator.hasNext()) {
            XSSFSheet sheet = (XSSFSheet) iterator.next();
            String rID = sheet.addRelation(null, XSSFRelation.IMAGES, workbook.getAllPictures().get(pictureIdx)).getRelationship().getId();
            //set background picture to sheet
            sheet.getCTWorksheet().addNewPicture().setId(rID);
        }
        //将XSSFWorkbook转化为InputStream
        return workbookConvertorStream(workbook);
    }

    /**
     * 将XSSFWorkbook转化为InputStream
     *
     * @param workbook XSSFWorkbook
     * @return InputStream
     */
    public static InputStream workbookConvertorStream(XSSFWorkbook workbook) {
        try {
            //临时缓冲区
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            //创建临时文件
            workbook.write(out);
            byte[] bookByteAry = out.toByteArray();
            return new ByteArrayInputStream(bookByteAry);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return null;
        }
    }

4.5 pptx

@SneakyThrows
    private static InputStream pptxAddWatermarkInputStream(InputStream input, TextWaterMarkDTO waterMarkDto) {
        XMLSlideShow slideShow = new XMLSlideShow(input);
        waterMarkDto.setPicWidth(Double.valueOf(slideShow.getPageSize().width).intValue());
        waterMarkDto.setPicHeight(Double.valueOf(slideShow.getPageSize().height).intValue());
        BufferedImage image = FontImage.createWatermarkImageFillWithText(waterMarkDto);
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        ImageIO.write(image, "png", os);
        PictureData pictureData1 = slideShow.addPicture(os.toByteArray(), PictureData.PictureType.PNG);
        for (XSLFSlide slide : slideShow.getSlides()) {
            XSLFPictureShape pictureShape = slide.createPicture(pictureData1);
            pictureShape.setAnchor(new Rectangle(0, 0, slideShow.getPageSize().width, slideShow.getPageSize().height));
        }
        return xmlSlideShowConvertorStream(slideShow);
    }

    /**
     * 将XMLSlideShow转化为InputStream
     *
     * @param slideShow slideShow
     * @return InputStream
     */
    public static InputStream xmlSlideShowConvertorStream(XMLSlideShow slideShow) {
        try {
            //临时缓冲区
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            //创建临时文件
            slideShow.write(out);
            byte[] bookByteAry = out.toByteArray();
            return new ByteArrayInputStream(bookByteAry);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return null;
        }
    }
  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
要使用Java中的BufferedImage给PPT添加背景图片水印,您可以使用Apache POI库来操作PPT文件。以下是一个示例代码: ```java import org.apache.poi.sl.usermodel.*; import org.apache.poi.xslf.usermodel.*; import org.apache.poi.util.IOUtils; import java.awt.*; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class AddBackgroundImageToPPT { public static void main(String[] args) { String pptFilePath = "path/to/presentation.pptx"; String backgroundImageFilePath = "path/to/background_image.jpg"; try (FileInputStream inputStream = new FileInputStream(pptFilePath); FileOutputStream outputStream = new FileOutputStream("path/to/output.pptx")) { XMLSlideShow ppt = new XMLSlideShow(inputStream); XSLFSlideMaster slideMaster = ppt.getSlideMasters().get(0); // 加载背景图片 BufferedImage backgroundImage = ImageIO.read(new File(backgroundImageFilePath)); int slideWidth = (int) slideMaster.getPageSize().getWidth(); int slideHeight = (int) slideMaster.getPageSize().getHeight(); // 创建一个带有背景图片PPT背景 XSLFBackground background = slideMaster.getBackground(); background.clear(); background.setFillType(BackgroundFill.FILL_PICTURE); background.setPictureData(ppt.addPicture(IOUtils.toByteArray(backgroundImage), PictureData.PictureType.JPEG)); background.setAnchor(new Rectangle2D.Double(0, 0, slideWidth, slideHeight)); // 遍历每一页PPT添加背景图片水印 for (XSLFSlide slide : ppt.getSlides()) { XSLFSlideLayout slideLayout = slide.getSlideLayout(); XSLFTextShape textShape = slideLayout.createTextBox(); XSLFTextParagraph paragraph = textShape.addNewTextParagraph(); XSLFTextRun textRun = paragraph.addNewTextRun(); // 设置水印文本样式和位置 textRun.setText("Watermark Text"); textRun.setFontSize(36); textRun.setFontColor(Color.GRAY); textShape.setAnchor(new Rectangle2D.Double(slideWidth / 2 - 200, slideHeight / 2 - 50, 400, 100)); // 将文本框添加到幻灯片 slide.getShapes().add(textShape); } ppt.write(outputStream); ppt.close(); } catch (IOException | IllegalArgumentException e) { e.printStackTrace(); } } } ``` 请确保您已将Apache POI库的依赖项添加到您的项目中。您可以在Maven或Gradle中添加以下依赖项: ```xml <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>4.1.2</version> </dependency> ``` 这是一个简单的示例,您可以根据您的需求进行修改和扩展。希望对您有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

随意石光

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值