如何在数据库只保存oss上的文件名, 当查询数据时根据字段的文件名, 获取oss的公网访问地址,并对字段内容重写
创始人
2024-02-12 15:11:42
0

如何在数据库只保存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 {@Autowiredprivate OssComponent ossComponent;@Autowiredprivate ThreadPoolTaskExecutor threadPoolTaskExecutor;@Overridepublic boolean supports(MethodParameter returnType, Class converterType) {return true;}@Overridepublic 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 fields = new HashMap<>();// 使用反射工具类 获取到指定对象中所有的属性,包含嵌套对象的属性ReflectUtil.getAllFields(data,fields);ArrayList> futures = new ArrayList<>();for (Field field : fields.keySet()) {// 遍历字段是否包含指定注解, OssConverter为自定义注解.OssConverter annotation = field.getAnnotation(OssConverter.class);if (annotation != null) {CompletableFuture 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 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 {// 地址转换的类型, 默认为ossUrlConverterType 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 deserialize;UrlConverterType(Function deserialize) {this.deserialize = deserialize;}
}

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

public class UrlConverterJsonSerializer extends JsonSerializer implements ContextualSerializer {private UrlConverterType urlConverterType;@Overridepublic 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);}}@Overridepublic 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吧.

相关内容

热门资讯

青草青青散文 青草青青散文(精选15篇)  在平平淡淡的日常中,许多人都写过散文吧?散文分为叙事散文、抒情散文、哲...
愿岁月温柔以待优秀散文 愿岁月温柔以待优秀散文(通用20篇)  无论是身处学校还是步入社会,大家都不可避免的会接触到散文吧?...
守住一颗宁静的心经典散文 守住一颗宁静的心经典散文  生活是一望无际的大海,人便是大海上的一叶扁舟,大海没有风平浪静的时候,所...
雪的短文散文 雪的短文散文  冬日的早晨,随着闹铃的响起,钻出暖暖的被窝,拉开窗帘。心中不觉掠过一阵惊喜:屋顶上、...
迪卡尔 中秋夜散文 迪卡尔 中秋夜散文  1  往年的中秋与今年的中秋并没有太大的差别,只是,对我来说,迪卡尔的出现,让...
散文近来可好 散文近来可好  曾经默默无言的静潜在同学群里,记得毕业那天,你在我的毕业留言本上绘着几株倩倩的青竹,...
酸石榴,甜石榴散文 酸石榴,甜石榴散文  老家院里种了两株石榴,约二十余年树龄,都有黝黑的粗干虬枝,一北一南,分据院子两...
爱情散文:有一种距离与爱无关 精选爱情散文:有一种距离与爱无关  两个人从陌生到相识,到有好感,到热恋,之间的距离愈来愈小;再到后...
妈妈别打我散文 妈妈别打我散文  初中毕业的时候,那时是07年,我做着08年去北京看奥运的打算。  不料,我去读了高...
自夸等于自贬散文 自夸等于自贬散文  一个人不能妄自菲薄,更不能妄自尊大,妄自菲薄的人看不到自身的优点,可怜可叹;妄自...
梁实秋《男人》散文 梁实秋《男人》散文  在生活、工作和学习中,大家都不可避免的会接触到散文吧?散文不讲究音韵,不讲究排...
菜花黄优美散文 菜花黄优美散文  在江南,过了春分,春色就越发的鲜艳了。最鲜艳的莫过于,田野里肆意铺陈的菜花。像是油...
鲁迅散文《二十四孝图》原文 《二十四孝图》读后感推荐度:二十四孝图读后感推荐度:寒窑赋原文解读推荐度:清明散文推荐度:我眼中的鲁...
朱自清春散文 朱自清春散文  朱自清,原名自华,号秋实,后改名自清,字佩弦。中国现代散文家、诗人、学者、民主战士。...
今夜请将我遗忘散文诗 今夜请将我遗忘散文诗  今夜请将我遗忘。一座青冢,衰草斜阳。我坐在坟头,低声吟唱,扑朔的蝙蝠为我送葬...
温柔如水的抒情散文 温柔如水的抒情散文  温柔如水,柔情似水,女人是水做的,天生就应该温柔善解人意;温柔体贴,默默关爱身...
闲话大维散文 闲话大维散文  看到这个题目,您可千万不要误会。俺可不是没事儿的时候,站在街头巷尾、胡同里弄、东家长...
我未入流抒情散文 我未入流抒情散文  爱人要出门,问我要不要买什么东西,她好顺便捎买回来。  我在看书,答曰没有,不要...
家是安身之所抒情散文 家是安身之所抒情散文  今天的天气是爽朗的,一大早便醒来。按说此时人的精神状态是最好的,毫无杂念,可...
城市里的菜园子优美散文 城市里的菜园子优美散文  我所居住的院子外面有一条未曾修建完缮的公路。因为只修了整个路面的一对半,那...