如何在数据库只保存oss上的文件名, 当查询数据时根据字段的文件名, 获取oss的公网访问地址,并对字段内容重写

news2024/9/23 5:25:53

如何在数据库只保存oss上的文件名, 当查询数据时根据字段的文件名, 获取oss的公网访问地址,并对字段内容重写.

有这样一个需求, 图片上传到oss 上, 返回文件名和公网访问地址, 但是要求数据库中只存储文件名称.

有两个目的:

  1. 数据库只存储文件名称, 方便后期oss 上数据迁移到其他对象存储上, 迁移保证文件名不变, 后期就只需要更改获取公网地址的地方.
  2. 在查询数据时再获取文件的访问地址, 并设置链接的有效期, 可以提高文件的安全性, 也可以防止oss 流量被盗刷.

实现的思路有两种方式

  • 都是基于java语言, 和springboot框架实现.

  • 都需要使用自定义注解.

  1. 在返回时, 使用反射的方式, 递归的扫描返回对象的每一个字段属性上是否添加该注解, 符合条件了, 获取到链接替换掉原本的值.
  2. 同样是在返回时, 只是在返回体序列化上进行处理.在jack序列化每一个字段属性的时候,扫描是否存在该注解,后续操作与方法一相同.
  • 利弊:

    方法一, 使用到了反射, 去层层扫描对象中的所有基本类型属性, 并且需要记录当前字段所属的对象, 否则在重写的时候,就不知道该属性的对象了. 方法二使用了jack字段序列化时,进行修改, 本身就避免了我们自己写反射扫描的问题, 而且方法二的效率比方法一快.

下面介绍两种方法的实现方式.

方法一:

自定义注解

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OssConverter {
}

需要定义一个 ResponseBodyAdvice的实现类

@Slf4j
@RestControllerAdvice(basePackages = "com.wdhcr") // 指定controller的路径, 可以范围大一点
public class ResponseBodyConfig implements ResponseBodyAdvice {

    @Autowired
    private OssComponent ossComponent;

    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;


    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {

        long start = System.currentTimeMillis();

        if (body instanceof R) {
            R result = (R) body;
            Object data = result.getData();
            Map<Field,Object> fields = new HashMap<>();
            // 使用反射工具类 获取到指定对象中所有的属性,包含嵌套对象的属性
            ReflectUtil.getAllFields(data,fields);
            ArrayList<CompletableFuture<Void>> futures = new ArrayList<>();
            for (Field field : fields.keySet()) {
              	// 遍历字段是否包含指定注解, OssConverter为自定义注解.
                OssConverter annotation = field.getAnnotation(OssConverter.class);
                if (annotation != null) {
                    CompletableFuture<Void> future = CompletableFuture.runAsync(() -> setImageUrlWithOssUrl(field, fields),threadPoolTaskExecutor);
                    futures.add(future);
                }
            }
            CompletableFuture[] completableFutures = new CompletableFuture[futures.size()];
            try {
              	// 想法是太多的网络请求, 可能影响返回数据效率, 所以使用了多线程方式.
                CompletableFuture.allOf(futures.toArray(completableFutures)).get();
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        }
        long end = System.currentTimeMillis();

        System.err.println("总计用时:" + ((end - start)));
        return body;
    }

    private void setImageUrlWithOssUrl(Field field, Map<Field, Object> fields) {

        Object value = null;
        try {
            field.setAccessible(true);
            value = field.get(fields.get(field));
            if (value != null) {
              	// 这个是oss 组件, 用来根据文件名获取访问地址的, 可见源码.
                String url = ossComponent.getUrl(value.toString());
                field.set(fields.get(field), url);
            }
        } catch (Exception e) {
            log.error("更新返回体中oss地址的字段异常,值为:{}", value);
        }

    }

}

上述类就是方法一的核心思想, 代码标有备注信息. 完整的demo,可查看文章末尾.

测试类(com.wdhcr.osspolicy.bean.User)和测试方法(com.wdhcr.osspolicy.controller.GetOssUrlBodyDemoController)可查看demo.

测试接口: http://localhost:8080/user

方法二

自定义注解

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@JacksonAnnotationsInside
@JsonSerialize(using = UrlConverterJsonSerializer.class)
public @interface OssConverterUrl {
  	// 地址转换的类型, 默认为oss
    UrlConverterType type() default UrlConverterType.OSS;

}

定义枚举类

@Getter
public enum UrlConverterType {

    OSS(url -> {
      	// 枚举的参数是一个lambda表达式
      	// url是入参
        OssComponent ossComponent = SpringUtils.getBean(OssComponent.class);
        return ossComponent.getUrl(url);
    });

		// 使用了java8的新特性,函数式接口
    private final Function<String, String> deserialize;

    UrlConverterType(Function<String, String> deserialize) {
        this.deserialize = deserialize;
    }
}

核心类, 就是上述注解中引用的UrlConverterJsonSerializer.class

public class UrlConverterJsonSerializer extends JsonSerializer<String> implements ContextualSerializer {

    private UrlConverterType urlConverterType;

    @Override
    public void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        if (Objects.nonNull(urlConverterType)) {
          	// 核心代码就是 json生成器写string时, 将原始属性值传入当前属性上的注解中的枚举, 获取到函数式接口并执行. (.apply(s)方法就会调用到枚举参数的lambda表达式了)
            jsonGenerator.writeString(urlConverterType.getDeserialize().apply(s));
        }else {
            jsonGenerator.writeString(s);
        }
    }

    @Override
    public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException {
      	
        OssConverterUrl converterUrl = beanProperty.getAnnotation(OssConverterUrl.class);
        if (Objects.nonNull(converterUrl) && Objects.equals(String.class, beanProperty.getType().getRawClass())) {
          	// 判断这个bean属性上OssConverterUrl自定义注解不为空, 并且该属性是string类型.
            this.urlConverterType = converterUrl.type();
            return this;
        }
        return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
    }
}

上述类就是方法一的核心思想, 代码标有备注信息. 完整的demo,可查看文章末尾.

测试类(com.wdhcr.osspolicy.bean.UserJson)和测试方法(com.wdhcr.osspolicy.controller.GetOssUrlBodyDemoController)可查看demo.

测试接口: http://localhost:8080/getUserJson

演示效果

在这里插入图片描述

项目地址: https://gitee.com/jack_whh/oss-policy-springboot 大家觉得还不错的话, 给个star吧.

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

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

相关文章

面试官:在 Java 中 new 一个对象的流程是怎样的?彻底被问懵了。。

对象怎么创建&#xff0c;这个太熟悉了&#xff0c;new一下(其实还有很多途径&#xff0c;比如反射、反序列化、clone等&#xff0c;这里拿最简单的new来讲)&#xff1a; Dog dog new Dog();我们总是习惯于固定语句的执行&#xff0c;却对于背后的实现过程缺乏认知&#xff0…

[附源码]java毕业设计医院门诊信息管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

【多线程 (二)】线程安全问题、同步代码块、同步方法、Lock锁、死锁

文章目录线程安全问题前言2.1多线程模拟卖票出现的问题2.2卖票案例中出现的问题分析2.3同步代码块解决数据安全问题2.4同步方法解决数据安全问题2.5Lock锁2.6死锁总结线程安全问题 前言 之前我们讲了多线程的基础知识&#xff0c;但是在我们解决实际问题中会遇到一些错误&…

接口自动化测试实战之智能场景如何攻破

智能场景的意思就是怎么样才能让接口自动化智能化&#xff0c;让使用接口框架的人越来越没有要求&#xff0c;大街上随便拉一个人来&#xff0c;一分钟了解框架的使用&#xff0c;就能完美地去完成接口自动化测试。 1.找出公司要求我们测试的接口的共同点 假设有10个接口&…

【附源码】计算机毕业设计JAVA移动电商网站

【附源码】计算机毕业设计JAVA移动电商网站 目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; JAVA mybati…

(一)进程与线程

黑马程序员深入学习Java并发编程&#xff0c;JUC并发编程全套教程_哔哩哔哩_bilibili 一、进程与线程&#xff08;P5&#xff09; 1. 进程 &#xff08;1&#xff09;程序由指令和数据组成&#xff0c;但这些指令要运行&#xff0c;数据要读写&#xff0c;就必须将指令加载至…

查阅标准文档以及effective c++作者文笔 真正搞懂万能引用和引用折叠以及完美转发

在解释任何东西以前 我都必须要强调 我们为什么需要这个东西 如果一个东西我们都是不需要的 那么我们解释他干嘛? 假定你彻底了解了一个东西 但是你并不知道你为什么需要他 他能解决什么问题 那你仅仅就只是背了一段理论性的东西 对于你本人的成长毫无用处 这里我们一次性讲懂…

sqli-labs/Less-58

这一关只有五次机会了 哎怎么办啊 那就只能找出每轮的共同点 这一关肯定不能一轮就完成所有的操作 至少得分个两轮进行操作才可以 前一轮进行注入类型的获取 后一轮进行各种爆破操作 分配好了 首先去判断一下注入类型是否属于数字型注入 输入如下 id1 and 12 回显如下 不属于…

Web大学生网页作业成品 基于HTML+CSS+JavaScript---个人介绍5页 带视频 带报告

⛵ 源码获取 文末联系 ✈ Web前端开发技术 描述 网页设计题材&#xff0c;DIVCSS 布局制作,HTMLCSS网页设计期末课程大作业 | ‍个人博客网站 | ‍个人主页介绍 | 个人简介 | 个人博客设计制作 | 等网站的设计与制作 | 大学生个人HTML网页设计作品 | HTML期末大学生网页设计作业…

大规模 Spring Cloud 微服务无损上下线探索与实践

作者&#xff1a;十眠 “从一次常见的发布说起&#xff0c;在云上某个系统应用发布时&#xff0c;重启阶段会导致较大数量的 OpenAPI、上游业务的请求响应时间明显增加甚至超时失败。随着业务的发展&#xff0c;用户数和调用数越来越多&#xff0c;该系统又一直保持一周发布二…

CAD特殊符号,你不一定会!!!

在CAD软件中&#xff0c;有时候会输入一些特殊的符号。比如在标明高低差的时候会输入“”号&#xff0c;在标明管子或者钢筋的直径为输入直径符号“”&#xff0c;为了标明角度值需要输入符号“”&#xff0c;那么这些符号怎么快速的绘制出来呢&#xff1f;我们一起用CAD梦想画…

专利解析|多维建模结合AI识别商品特征的方法

企业采购数字化转型的背景 国家“十四五”规划纲要提出要推进产业数字化转型&#xff0c;在供给侧结构性改革大背景下&#xff0c;国家出台了《企业数字化采购实施指南》&#xff0c;大大促进了企业采购电商化的发展。企业电商化采购能提高企业的采购效率、加快物流速度、降低…

m基于QPSK调制解调的无线图像传输matlab仿真,包括扩频解扩均衡等模块

目录 1.算法描述 2.仿真效果预览 3.MATLAB部分代码预览 4.完整MATLAB程序 1.算法描述 软件无线电在无线通信领域被称为是自模拟通信过渡到数字通信之后的又一次革命&#xff0c;在军用和民用方面都有着广阔的应用。它是一种新的无线通信技术&#xff0c;基于通用的可编程的…

【JAVA高级】——封装JDBC中的DaoUtils工具类(Object类型方法)

✅作者简介&#xff1a;热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏&#xff1a;JAVA开发者…

【文献整理】基于深度强化学习的知识图谱推理研究

目录DeepPath背景Core贡献几个要点&#xff1a;Training pipeline结论DIVINE背景Core贡献预备知识DIVINE推理过程模型文献整理基于综述论文&#xff1a;基于深度强化学习的知识推理研究进展综述_宋浩楠&#xff0c;赵刚&#xff0c;孙若莹 文中对知识图谱推理进行如下分类&…

SpringSecurity(十七)---OAuth2的运行机制(下)-实现一个简单的单点登录应用程序

一、前言 本章实现第一个使用带有Spring Boot和Spring Security 的OAuth2框架的应用程序。这个示例将展示如何将OAuth2应用到Spring Security中&#xff0c;并阐释你需要了解的一些接口的内容。顾名思义&#xff0c;单点登录&#xff08;SSO&#xff09;应用程序是通过授权服务…

如何使用一台电脑远程控制多台电脑

如今&#xff0c;远程控制软件已经广泛应用于我们的日常生活中。我们使用远程桌面软件远程控制另一台电脑来完成我们的工作和学习。在某些情况下&#xff0c;我们可能还需要同时远程控制多台电脑。例如&#xff1a; 您是一名培训师&#xff0c;正在寻找远程访问软件来同时远程…

[激光原理与应用-15]:《激光原理与技术》-1- 什么是激光,激光概述

目录 第1章 什么是激光 1.1 什么是激光 1.2激光在生活中应用 第2章 激光的特点 2.1 方向性好&#xff08;平行性、直线性&#xff09; 2.2 单色性好&#xff08;颜色纯度高&#xff09; 2.3 相干性比太阳光好 2.4 亮度高 2.5 能量极大 第3章 光产生的方式与核心概念 …

又爆冷了啦,日本半场逆转德国,怎么利用共享经济搅乱世界杯格局

近日世界杯热点逐渐升高&#xff0c;在23号晚上亚洲劲旅日本以2-1逆转多次捧得大力神杯的德国队&#xff0c;此前德国还从未输过日本队&#xff0c;因此德国再次吃到闭门羹&#xff0c;爆出了本届世界杯开赛以来既阿根廷惨败的又一大冷门。赛后&#xff0c;日本全国人民共同庆祝…

Web(二)html5基础-超链接的应用(知识训练和编程训练)

web知识训练_html5_超链接的应用 web编程训练_html5_超链接的应用 第1关_创建热字超链接 编程要求 在右侧编辑器中的Begin - End区域内补充代码&#xff0c;创建热字超链接&#xff0c;具体要求是&#xff1a; 1.链源文字为“听音乐找酷我”。 2.链宿地址为“https://www.ku…