Springboot 结合PDF上传到OSS

news2024/11/19 0:24:59

目录

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

二、步骤

2.1 将pdf模板上传到oos

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

​编辑 三、代码

3.1 pom.xml

3.2 配置文件

 3.3 oss model

3.4 配置类(不需要修改)

3.5 将配置类放入ioc容器

3.6 controller

四、结果

​编辑 五、源代码参考


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

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

二、步骤

2.1 将pdf模板上传到oos

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

 三、代码

3.1 pom.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 配置文件

# 配置阿里云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

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 配置类(不需要修改)

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容器

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1614074.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux之rocky8操作系统安装

一、rocky系统简介 CentOS宣布停止开发后&#xff0c;CentOS的原创始人Gregory Kurtzer在CentOS网站上发表评论宣布&#xff0c;他将再次启动一个项目以实现CentOS的最初目标。它的名字被选为对早期CentOS联合创始人Rocky McGaugh的致敬。rocky系统一个开源、社区拥有和管理、免…

VForm3的文件上传方式

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码&#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 演示地址&#xff1a;RuoYi-Nbcio后台管理系统 http://122.227.135.243:9666/ 更多nbcio-boot功能请看演示系统 gitee源代码地址 后端代码&#xff1a…

快排非递归与计数排序

感谢大佬的光临各位&#xff0c;希望和大家一起进步&#xff0c;望得到你的三连&#xff0c;互三支持&#xff0c;一起进步 个人主页&#xff1a;LaNzikinh-CSDN博客 收入专栏:初阶数据结构_LaNzikinh篮子的博客-CSDN博客 文章目录 前言一.快速排序非递归二.数据结构栈与内存栈…

“电子商务”的红利还能存在多久?我想抖音回答了这个问题

哈喽~我是电商月月 互联网的发展有目共睹&#xff0c;网上交易&#xff0c;电子支付已经遍及每个人身边&#xff0c;两者结合&#xff0c;特别是电商行业&#xff0c;利用这个时期真的赚的盆满钵满 每年都有大批商家涌入电商这个赛道&#xff0c;但发展了那么多年&#xff0c…

B端界面:除了蓝色外,四条搞定清新明快的界面设计。

一、什么是清新明快风格 清新明快的设计风格是指在B端系统中使用明亮、清淡的色彩、简洁的布局和自然元素&#xff0c;以及轻快的动效&#xff0c;营造出轻松、愉悦的界面氛围。 二、哪些行业适用 这种设计风格适用于多个行业&#xff0c;特别是那些与创意、娱乐、健康、旅游…

Python turtle海龟绘制美国队长盾牌

使用Python中的turtle模块绘制美队盾牌 具体思路如下&#xff1a; 导入海龟库第1个圆&#xff1a;半径 200&#xff0c;红色填充第2个圆&#xff1a;半径 150&#xff0c;白色填充第3个圆&#xff1a;半径 100&#xff0c;红色填充第4个圆&#xff1a;半径 50&#xff0c;蓝色…

动态Web项目讲解+Demo

web流程演示 请求路径 请求路径明确要请求的是哪个servlet 请求方式 servlet含有两种请求方式&#xff1a;doGet和doPost doGet&doPost 返回数据就是httpResponse&#xff0c;返回给success 参数 包含在request当中 成功 上述流程任何一步都没出问题&#xff0c;就会…

Java学习笔记26(枚举和注解)

1.枚举和注解 1.1 枚举 ​ 1.枚举(enumeration) ​ 2.枚举是一组常量的集合 ​ 3.枚举属于一种特殊的类&#xff0c;里面只包含一组有限的特定的对象 1.枚举应用案例 ​ 1.不需要提供setXxx方法&#xff0c;因为枚举对象值通常为只读 ​ 2.对枚举对象/属性使用final st…

完成学校官网页面制作

<!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <title>教务系统</title> <style> .wap{ margin:0 auto; width:955px; } .top{ height:150px; padding-left:85px; …

【PCI】PCIe EP标准配置空间(十)

本文参考PCIe协议 5.0&#xff1a;https://download.csdn.net/download/zz2633105/89204842 PCIe配置空间 EP标准配置空间 Vendor ID Register (Offset 00h) PCIe设备厂商ID&#xff0c;比如Intel的Vendor ID通常是0x8086。需要注意的是&#xff0c;厂商的ID不是厂商随意定的…

人人可拥有刘强东同款数字人分身!

每个人都可以拥有东哥同款数字人分身直播间进行直播带货&#xff0c;怎样克隆自己的数字人形象&#xff1f; 青否数字人克隆源码的克隆效果媲美真人&#xff1a; 仅需将真人录制的2-6分钟视频上传至克隆端后台&#xff0c;系统便会自动启动自动克隆。3-5小时后&#xff0c;即可…

Python从0到100(十五):函数的高级应用

前言&#xff1a; 零基础学Python&#xff1a;Python从0到100最新最全教程。 想做这件事情很久了&#xff0c;这次我更新了自己所写过的所有博客&#xff0c;汇集成了Python从0到100&#xff0c;共一百节课&#xff0c;帮助大家一个月时间里从零基础到学习Python基础语法、Pyth…

地图图源#ESRI ArcGIS XYZ Tiles系列(TMS)

目录 1、前言 2、地图图源网址 2.1、Satellite 卫星图源 2.2、Terrain 地形图源 2.3、Street 路网/标注图源 2.4、Specifity 特色设计图源 3、专业推荐”穿搭“ 4、图源配置下载及使用 图源名称图层类别特别注意谷歌 Google①地形 ②影像 ③矢量及标注 ④特色图源国内大…

迭代加深搜索(图的路径查找)

目录 概念 优点 缺点 如何剪枝&#xff08;八数码&#xff09; 剪枝策略&#xff1a; 深度优先搜索&#xff08;DFS&#xff09;和广度优先搜索&#xff08;BFS&#xff09; 深度优先搜索&#xff08;DFS&#xff09; 广度优先搜索&#xff08;BFS&#xff09; 比较 应…

Facebook账号运营要用什么IP?

众所周知&#xff0c;Facebook封号大多数情况都是因为IP的原因。Facebook对于用户账号有严格的IP要求和限制&#xff0c;以维护平台的稳定性和安全性。在这种背景下&#xff0c;海外IP代理成为了一种有效的解决方案&#xff0c;帮助用户避免检测&#xff0c;更加快捷安全地进行…

基于51单片机的电子秤LCD1602液晶显示( proteus仿真+程序+设计报告+讲解视频)

基于51单片机电子秤LCD显示 1. 主要功能&#xff1a;2. 讲解视频&#xff1a;3. 仿真设计4. 程序代码5. 设计报告6. 设计资料内容清单&&下载链接 基于51单片机电子秤LCD显示( proteus仿真程序设计报告讲解视频&#xff09; 仿真图proteus8.9及以上 程序编译器&#xf…

OceanBase开发者大会2023届视频及PPT汇总

数据库技术趋势 我眼中的数据库技术 阳振坤OceanBase 首席科学家 观看视频 下载 PDF 未来&#xff0c;中国需要什么样的数据库&#xff1f; 周傲英华东师范大学副校长&#xff0c;CCF 会士 观看视频 下载 PDF 云原生技术趋势解读 Keith ChanCNCF 云原生计算基金会中国区总监 …

Linux嵌入式驱动开发-阻塞IO与非阻塞IO

文章目录 阻塞与非阻塞访问简介阻塞访问的实现等待队列等待队列头等待队列项从等待队列头添加/移除等待队列项等待唤醒等待事件API 非阻塞访问的实现轮询poll 函数原型可以返回的资源状态 阻塞与非阻塞访问简介 **IO&#xff1a;**Input/Output&#xff0c;也就是输入/输出&am…

【无标题】vscode 配置c++ c编译环境

不用图形化也可以直接把launcher.json c_c_properties.json task.json复制到项目里 首先打开 vscode创建项目 ctrlshiftp 打开c/c edit configuration UI 配置生成c_cpp_properties.json文件 这里选择gcc为 c运行环境 只需要改配置名称跟编译器路径两处其他默认 选g为c环境 可…

Docker容器化部署(企业版)

大家好&#xff0c;webfunny前端监控埋点系统&#xff0c;已经正式发布了webfunny的官方镜像&#xff1a; Webfunny镜像目录&#xff1a;https://hub.docker.com/r/webfunny/webfunny_monitor_cluster/tags 部署前提是你的服务器已经安装了Docker环境&#xff0c;没有安装doc…