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

相关推荐
墨染辉7 小时前
10.2 如何解决从复杂 PDF 文件中提取数据的问题?
pdf
shandianchengzi12 小时前
【记录】Excel|Excel 打印成 PDF 页数太多怎么办
pdf·excel
bianshaopeng20 小时前
android 原生加载pdf
android·pdf
卢卡斯23321 小时前
在线PDF怎么转换成JPG图片?分享14种转换操作!
pdf
J不A秃V头A1 天前
iTextPDF中,要实现表格中的内容在数据长度超过边框时自动换行
java·pdf
嘻嘻仙人2 天前
【杂谈一之概率论】CDF、PDF、PMF和PPF概念解释与分析
pdf·概率论·pmf·cdf
资深前端之路2 天前
vue2 将页面生成pdf下载
前端·vue.js·pdf
Eiceblue2 天前
Python 复制PDF中的页面
vscode·python·pdf
J不A秃V头A3 天前
使用iTextPDF库设置文字为英文样式
java·pdf
Odoo穆尘3 天前
【零散技术】Odoo PDF 打印问题问题合集
pdf·odoo