Springboot 结合PDF上传到OSS

目录

一、首先注册阿里云OSS(新用户免费使用3个月)

二、步骤

[2.1 将pdf模板上传到oos](#2.1 将pdf模板上传到oos)

[2.2 这里有pdf地址,将读写权限设置为共工读](#2.2 这里有pdf地址,将读写权限设置为共工读)

[​编辑 三、代码](#编辑 三、代码)

[3.1 pom.xml](#3.1 pom.xml)

[3.2 配置文件](#3.2 配置文件)

[3.3 oss model](#3.3 oss model)

[3.4 配置类(不需要修改)](#3.4 配置类(不需要修改))

[3.5 将配置类放入ioc容器](#3.5 将配置类放入ioc容器)

[3.6 controller](#3.6 controller)

四、结果

[​编辑 五、源代码参考](#编辑 五、源代码参考)


一、首先注册阿里云OSS(新用户免费使用3个月)

阿里云OSS 存储对象的注册与使用-CSDN博客

二、步骤

2.1 将pdf模板上传到oos

2.2 这里有pdf地址,将读写权限设置为共工读

三、代码

3.1 pom.xml

XML 复制代码
   <!--导入支持pdf的依赖 -->
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itext7-core</artifactId>
            <version>7.2.5</version>
            <type>pom</type>
        </dependency>
 <!--引入阿里云oss依赖 -->
        <dependency>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
            <version>3.10.2</version>
        </dependency>

3.2 配置文件

XML 复制代码
# 配置阿里云OSS(application.properties)
aliyun.oss.bucketName = hyc-8147
aliyun.oss.endpoint = oss-cn-beijing.aliyuncs.com
aliyun.oss.accessKeyId = LTAI5tE2krid8AXzidDUpn9n
aliyun.oss.accessKeySecret = 2A0Vrvj982nfRPWDVt3lp

# yml版(application.yml)
#aliyun:
#  oss:
#    bucketName: hyc-8147
#    endpoint: oss-cn-beijing.aliyuncs.com
#    accessKeyId: LTAI5tE2krid8AXzidDUpn9n
#    accessKeySecret: 2A0Vrvj982nfRPWDVt3lp

3.3 oss model

java 复制代码
package com.by.model;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
 
@Configuration
@ConfigurationProperties(prefix = "aliyun.oss")
@Data
public class AliOssProperties {
 
    private String endpoint;
    private String accessKeyId;
    private String accessKeySecret;
    private String bucketName;
 
 
}

3.4 配置类(不需要修改)

java 复制代码
package com.by.util;

import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.io.ByteArrayInputStream;
 
@Data
@AllArgsConstructor
//固定代码,CV直接使用
public class AliOssUtil {
 
    private String endpoint;
    private String accessKeyId;
    private String accessKeySecret;
    private String bucketName;
 
    /**
     * 文件上传
     *
     * @param bytes :传入的文件要转为byte[]
     * @param objectName :表示在oss中存储的文件名字。
     * @return
     */
    public String upload(byte[] bytes, String objectName) {
 
        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
 
        try {
            // 创建PutObject请求。
            ossClient.putObject(bucketName, objectName, new ByteArrayInputStream(bytes));
        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
        } catch (ClientException ce) {
            System.out.println("Caught an ClientException, which means the client encountered "
                    + "a serious internal problem while trying to communicate with OSS, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message:" + ce.getMessage());
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
 
        //文件访问路径规则 https://BucketName.Endpoint/ObjectName
        StringBuilder stringBuilder = new StringBuilder("https://");
        stringBuilder
                .append(bucketName)
                .append(".")
                .append(endpoint)
                .append("/")
                .append(objectName);
 
        return stringBuilder.toString();
    }
}

3.5 将配置类放入ioc容器

java 复制代码
package com.by.config;


import com.by.model.AliOssProperties;
import com.by.util.AliOssUtil;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class OssConfiguration {
    @Bean
    @ConditionalOnMissingBean
    public AliOssUtil getAliOssUtil(AliOssProperties aliOssProperties) {
//        log.info("创建OssUtil");
        AliOssUtil aliOssUtil = new AliOssUtil(
                aliOssProperties.getEndpoint(),
                aliOssProperties.getAccessKeyId(),
                aliOssProperties.getAccessKeySecret(),
                aliOssProperties.getBucketName()
        );
        return aliOssUtil;
    }
}

3.6 controller

java 复制代码
package com.by.controller;

import com.by.util.AliOssUtil;
import com.itextpdf.forms.PdfAcroForm;
import com.itextpdf.forms.fields.PdfFormField;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.PdfWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

@RestController
public class PdfController {
    // 初始化日志记录器,用于记录PDF控制器类的操作日志
    private static final Logger logger = LoggerFactory.getLogger(PdfController.class);

    @Autowired
    private AliOssUtil aliOssUtil;

    /**
     * 生成填充数据的PDF文件并提供下载。
     *
     * @param response 用于设置HTTP响应信息的ServletResponse对象。
     * @return 返回包含填充后PDF文件内容的响应实体。
     * @throws IOException 如果处理PDF文件或下载模板文件时发生IO错误。
     */
    @GetMapping("/download")
    public ResponseEntity<byte[]> generateFilledPdf(HttpServletResponse response) throws IOException {
        // 准备需要填充到PDF的数据
        Map<String, String> dataMap = new HashMap<>();
        dataMap.put("name", "黄哥");
        dataMap.put("tel", "175");

        // 从URL下载PDF模板并临时保存到本地
        String templateUrl = "https://hyc-8147.oss-cn-beijing.aliyuncs.com/3.pdf";
        Path tempTemplateFile = Files.createTempFile("temp_template_", ".pdf");

        try (InputStream inputStream = new URL(templateUrl).openStream()) {
            Files.copy(inputStream, tempTemplateFile, StandardCopyOption.REPLACE_EXISTING);
        } catch (IOException e) {
            logger.error("Failed to download and save the PDF template from {}", templateUrl, e);
            // 下载模板失败时,返回500错误并提供错误信息
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body("Error downloading PDF template. Please try again later.".getBytes());
        }
        try {
            // 使用填充的数据生成新的PDF文件
            byte[] pdfBytes = fillPdfData(tempTemplateFile, dataMap);

            // 将生成的PDF文件上传到OSS,并设置下载文件名
            String downloadFileName = System.currentTimeMillis() + "_filled.pdf";
            aliOssUtil.upload(pdfBytes, downloadFileName);

            // 设置响应头,提供PDF文件的下载
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_PDF);
            headers.setContentDispositionFormData("attachment", downloadFileName);
            return ResponseEntity.ok().headers(headers).body(pdfBytes);
        } finally {
            // 清理临时文件
            Files.deleteIfExists(tempTemplateFile);
        }
    }


    /**
     * 根据提供的数据映射,填充PDF表单并返回填充后的PDF数据。
     *
     * @param sourcePdf 表单源PDF文件的路径。
     * @param dataMap   需要填充到PDF表单的数据映射,键为表单字段名称,值为填充的文本。
     * @return 填充后的PDF数据的字节数组。
     * @throws IOException 如果读取或处理PDF文件时发生错误。
     */
    private byte[] fillPdfData(Path sourcePdf, Map<String, String> dataMap) throws IOException {
        // 使用try-with-resources语句确保资源正确关闭
        try (InputStream fileInputStream = Files.newInputStream(sourcePdf);
             PdfReader pdfReader = new PdfReader(fileInputStream);
             ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {

            // 初始化PDF文档并设置页面大小
            PdfDocument pdf = new PdfDocument(pdfReader, new PdfWriter(outputStream));
            pdf.setDefaultPageSize(PageSize.A4);

            // 获取PDF表单并填充数据
            PdfAcroForm form = PdfAcroForm.getAcroForm(pdf, true);
            Map<String, PdfFormField> fields = form.getFormFields();

            // 设置表单字段使用的字体
            PdfFont currentFont = PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H", PdfFontFactory.EmbeddingStrategy.PREFER_NOT_EMBEDDED);

            // 填充表单字段
            dataMap.forEach((key, value) -> {
                Optional.ofNullable(fields.get(key))
                        .ifPresent(formField -> formField.setFont(currentFont).setValue(value));
            });

            // 将表单字段合并到PDF文档中,防止编辑
            form.flattenFields();

            // 关闭PDF文档并返回填充后的PDF数据
            pdf.close();

            return outputStream.toByteArray();
        } catch (Exception e) {
            logger.error("Error filling PDF data: {}", e.getMessage());
            // 抛出IOException,封装原始异常
            throw new IOException("Failed to fill PDF data due to an internal error.", e);
        }
    }

}

四、结果

五、源代码参考

源代码我已经放入了云效

https://codeup.aliyun.com/62858d45487c500c27f5aab5/huang-spring-boot-pdf.git

相关推荐
m0_748230949 小时前
SpringBoot实战(三十二)集成 ofdrw,实现 PDF 和 OFD 的转换、SM2 签署OFD
spring boot·后端·pdf
程序员WANG15 小时前
论文+AI赋能教育:探索变革路径与创新实践。包括word和pdf格式。
人工智能·学习·pdf·教育·变革
風落18 小时前
《告别复杂PDF编辑,PDF Eraser开启便捷办公新体验》
pdf·软件工程·软件需求
b_qixin1 天前
文档解析:PDF里的复杂表格、少线表格如何还原?
人工智能·pdf
花生糖@1 天前
Python实现PDF文档转图片功能
pdf
圣道寺2 天前
审计文件标识作为水印打印在pdf页面边角
java·前端·python·pdf·学习方法
baivfhpwxf20232 天前
c# PDF文件合并工具
pdf
拓端研究室2 天前
2024微短剧行业生态洞察报告汇总PDF洞察(附原数据表)
pdf
码上艺术家2 天前
手摸手系列之 Java 通过 PDF 模板生成 PDF 功能
java·开发语言·spring boot·后端·pdf·docker compose
ghostwritten2 天前
实战经验:使用 Python 的 PyPDF 进行 PDF 操作
android·python·pdf