【第4期】Springboot集成阿里云对象存储OSS+Vue+Iview文件上传组件

本期简介

文件上传是非常常见的功能,本期要实现的功能是将文件存储到阿里云分布式对象存储OSS中,这样做的好处是随便哪里都可以方便的展示出该图片,并且图片以链接形式在客户端浏览器渲染,流量不会经过后台,降低后台压力,但若对文件有限制性的操作,还是要通过后台返回文件

  • 本期要点:
  1. 配置阿里云OSS密钥
  2. 编写OSS上传的简单类
  3. Springboot集成OSS
  4. 后端开发上传接口
  5. 前端开发Vue+Iview+Uploader组件
  6. 前端渲染上传后的图片

一、配置阿里云OSS密钥及桶

1、购买链接

如果没有云存储,可访问阿里云官网,搜索对象存储或者访问下方链接,按需购买
https://common-buy.aliyun.com/?spm=5176.7933691.J_5253785160.2.64e62c47OY978E&commodityCode=oss_rc_dp_cn

2、创建桶

对象存储是以桶来进行文件管理,文件都存在一个个的桶里,每个桶可以存放目录和文件,即可以按照文件进行分类存储,可以创建多个桶。
在这里插入图片描述

从下图可以看到,一个桶里面可以存储多个文件夹,文件夹里可以存多个文件,桶里面也可以直接存文件
在这里插入图片描述

二、配置密钥

要想将文件上传到OSS中,用的是AK/SK的方式进行认证授权,所以需要先在阿里云OSS中配置安全密钥,

  • 点击头像打开访问控制页面
    在这里插入图片描述

  • 创建用户
    在这里插入图片描述

  • 创建访问AccessKey
    需要注意的是,创建的时候AppSecret及时复制出来保存,关闭弹窗后,无法再从阿里云页面找到,若不慎丢失,需重新生成
    在这里插入图片描述
    至此,云存储已经具备了,访问的AK/SK也有了,接下来编写客户端上传下载的逻辑。

三、编写OSS上传的简单类

1、将AK、SK配置到环境变量

在这里插入图片描述

2、添加maven依赖

        <dependency>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
            <version>3.15.1</version>
        </dependency>

3、初始化OSS配置

  • 配置类
public class OssConfig {
    private String accessKeyId;

    private String secretAccessKey;

    private String endpoint;

    private String bucketName;
}
  • 初始化OSS
private final DefaultCredentialProvider credentialsProvider;

    private final OssConfig ossConfig;

    @SneakyThrows
    public OssHelper(@Autowired OssConfig ossConfig) {
        // 从环境变量中获取访问凭证。运行本代码示例之前,请先配置环境变量。
        if (ossConfig == null) {
            throw new ServerException("初始化对象存储工具失败,配置信息未读取到");
        }
        this.ossConfig = ossConfig;
        this.credentialsProvider = CredentialsProviderFactory.newDefaultCredentialProvider(ossConfig.getAccessKeyId(), ossConfig.getSecretAccessKey());
    }

3、上传方法

OSS支持上传的类型非常多,这里例举几类

  • 上传本地二进制文件
public Optional<String> upload(File file, String ossDir, String ossFileName) {
        try {
            return this.uploadToOss(file, ossDir, ossFileName);
        } catch (Exception exception) {
            log.error("上传字符串前异常:{}", exception.getMessage());
        }
        return Optional.empty();
    }
  • 上传文本
    public Optional<String> upload(String content, String ossDir, String ossFileName) {
        try {
            InputStream inputStream = new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8));
            return this.uploadToOss(inputStream, ossDir, ossFileName);
        } catch (Exception exception) {
            log.error("上传字符串前异常:{}", exception.getMessage());
        }
        return Optional.empty();
    }
  • 上传字节类
    public Optional<String> upload(byte[] content, String ossDir, String ossFileName) {
        try {
            InputStream inputStream = new ByteArrayInputStream(content);
            return this.uploadToOss(inputStream, ossDir, ossFileName);
        } catch (Exception exception) {
            log.error("上传字符串前异常:{}", exception.getMessage());
        }
        return Optional.empty();
    }
  • 上传网络资源
    public Optional<String> upload(URL url, String ossDir, String ossFileName) {
        try {
            if (url == null) {
                return Optional.empty();
            }
            InputStream inputStream = url.openStream();
            return this.uploadToOss(inputStream, ossDir, ossFileName);
        } catch (Exception exception) {
            log.error("上传字符串前异常:{}", exception.getMessage());
        }
        return Optional.empty();
    }
  • 上传核心业务逻辑
    private Optional<String> uploadToOss(Object object, String ossDir, String ossFileName) {
        // 填写Object完整路径,例如exampledir/exampleobject.txt。Object完整路径中不能包含Bucket名称。
        OSS ossClient = new OSSClientBuilder().build(ossConfig.getEndpoint(), credentialsProvider);
        try {
            String objectName = getObjectName(ossDir, ossFileName);
            PutObjectRequest putObjectRequest = null;
            if (object instanceof InputStream) {
                putObjectRequest = new PutObjectRequest(ossConfig.getBucketName(), objectName, (InputStream) object);
            }
            if (object instanceof File) {
                putObjectRequest = new PutObjectRequest(ossConfig.getBucketName(), objectName, (InputStream) object);
            }
            if (putObjectRequest == null) {
                return Optional.empty();
            }
            PutObjectResult result = ossClient.putObject(putObjectRequest);
            // 设置签名URL过期时间,单位为毫秒。本示例以设置过期时间为50年为例。
            Date expiration = new Date(new Date().getTime() + 50 * 365 * 24 * 3600 * 1000L);
            // 生成以GET方法访问的签名URL,访客可以直接通过浏览器访问相关内容。
            URL url = ossClient.generatePresignedUrl(ossConfig.getBucketName(), objectName, expiration);
            if (url != null) {
                return Optional.of(url.toString());
            }
            return Optional.empty();
        } catch (OSSException oe) {
            log.error("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            log.error("Error Message:" + oe.getErrorMessage());
            log.error("Error Code:" + oe.getErrorCode());
            log.error("Request ID:" + oe.getRequestId());
            log.error("Host ID:" + oe.getHostId());
        } catch (ClientException ce) {
            log.error("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.");
            log.error("Error Message:" + ce.getMessage());
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
        return Optional.empty();
    }

4、测试上传与验证

从下图可以看到,成功把字符串hello world上传到了OSS的certificate目录下,文件名为test.txt
在这里插入图片描述

查看OSS桶的对应文件内容,与上传一致
在这里插入图片描述

5、下载方法

public void download(String ossDir, String ossFileName) {
        String objectName = getObjectName(ossDir, ossFileName);
        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(ossConfig.getEndpoint(), credentialsProvider);

        try {
            // 调用ossClient.getObject返回一个OSSObject实例,该实例包含文件内容及文件元信息。
            OSSObject ossObject = ossClient.getObject(ossConfig.getBucketName(), objectName);
            // 调用ossObject.getObjectContent获取文件输入流,可读取此输入流获取其内容。
            InputStream content = ossObject.getObjectContent();
            if (content != null) {
                BufferedReader reader = new BufferedReader(new InputStreamReader(content));
                while (true) {
                    String line = reader.readLine();
                    if (line == null) {
                        break;
                    }
                    System.out.println("\n" + line);
                }
                // 数据读取完成后,获取的流必须关闭,否则会造成连接泄漏,导致请求无连接可用,程序无法正常工作。
                content.close();
            }
        } 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();
            }
        }
    }

6、测试下载与验证

从OSS中将前面上传的test.txt下载下来打印出内容,与上传时的一致
在这里插入图片描述

四、springboot集成OSS

到了这一步,上传、下载等功能已经具备了,只需要将其与Springboot集成起来,即可在各个业务中使用,为了使用单例模式,这里将其注册为Spring Bean

1、配置类

@Data
@Configuration
@ConfigurationProperties(prefix = "aliyun.oss")
public class OssConfig {
    private String accessKeyId;

    private String secretAccessKey;

    private String endpoint;

    private String bucketName;
}

2、application.yml配置

在这里插入图片描述

3、OSS核心类

@Slf4j
@Component
public class OssHelper {

    private final DefaultCredentialProvider credentialsProvider;

    private final OssConfig ossConfig;

    @SneakyThrows
    public OssHelper(@Autowired OssConfig ossConfig) {
        // 从环境变量中获取访问凭证。运行本代码示例之前,请先配置环境变量。
        if (ossConfig == null) {
            throw new ServerException("初始化对象存储工具失败,配置信息未读取到");
        }
        this.ossConfig = ossConfig;
        this.credentialsProvider = CredentialsProviderFactory.newDefaultCredentialProvider(ossConfig.getAccessKeyId(), ossConfig.getSecretAccessKey());
    }

	public Optional<String> upload(File file, String ossDir, String ossFileName) {
	.........
	}
    public Optional<String> upload(String content, String ossDir, String ossFileName) {
	...........
	}
    
    public Optional<String> upload(byte[] content, String ossDir, String ossFileName) {
 	............
	}
    public Optional<String> upload(URL url, String ossDir, String ossFileName) {
	............
	}
    private Optional<String> uploadToOss(Object object, String ossDir, String ossFileName) {
	............
	}
    public void download(String ossDir, String ossFileName) {
	............
	}
}

五、后端开发上传逻辑

1、编写接口,入参只有一个MultipartFile

在这里插入图片描述

2、上传逻辑

这里是直接获取到MultipartFile的字节数组,将其上传到OSStest/cover目录下,文件名是通过雪花算法生成的id
在这里插入图片描述
至此,后端已经具备文件上传的完整功能,接下来编写前端的上传功能

六、前端开发Vue+Iview+Uploader组件

1、使用Iview Uploader

该组件可以支持拖拽和选择上传,功能方便,实现简单

          <FormItem label="封面" prop="coverId">
            <Upload
              ref="upload"
              :show-upload-list="false"
              :format="['zip','png','jpg','jpeg','pdf','doc','docx','ppt','pptx','xls','xlsx']"
              :max-size="5120"
              :on-success="handleUploadSuccess"
              :on-error="handleUploadFailed"
              :on-format-error="handleFormatError"
              :on-exceeded-size="handleMaxSize"
              :before-upload="handleBeforeUpload"
              style="width: 210px;height:50px;"
              type="drag"
              :action="albumCoverAction"
              :headers="uploadHeader">
              <div>
                <Icon type="ios-cloud-upload" size="30" style="color: #3399ff"></Icon>
                <p>点击上传或将图片拖拽到这里</p>
              </div>
            </Upload>
            <RadioGroup v-model="formData.coverId">
              <Row style="margin-top: 40px;margin-left: 0px;">
                <Col span="4">
                  <div>
                    <Card dis-hover style="height:auto;width:210px;">
                      <p slot="title">自定义封面</p>
                      <Row>
                        <Col span="24">
                          <Radio :label="customCover.id">
                            <div :class="selectedCover===customCover.id?'album-cover-selected':'album-cover'"
                                 @click="changeCover(customCover)"
                                 style="margin-right: 10px;">
                              <img :src="customCover.coverUrl"/>
                            </div>
                          </Radio>
                        </Col>
                      </Row>
                    </Card>
                  </div>
                </Col>
                <Col span="19" offset="1">
                  <Card dis-hover style="height:auto">
                    <p slot="title">系统默认封面</p>
                    <Row>
                      <Col span="24">
                        <Radio v-for="(item, index) in $store.state.defaultAlbumCovers" class="cover-default"
                               :key="index"
                               :label="item.id">
                          <div :class="selectedCover===item.id?'album-cover-selected':'album-cover'"
                               @click="changeCover(item)"
                               style="margin-right: 5px;">
                            <img :src="item.coverUrl"/>
                          </div>
                        </Radio>
                      </Col>
                    </Row>
                  </Card>
                </Col>
              </Row>
            </RadioGroup>
          </FormItem>

2、上传地址和鉴权

上面的上传组件中的文件往哪里上传,以及怎么通过后端的用户登录认证,是通过如下2个值来决定的
在这里插入图片描述

七、前端渲染上传后的图片

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【23.12.29期--Spring篇】Spring的 IOC 介绍

介绍一下Spring的IOC ✔️引言✔️ lOC的优点✔️Spring的IOC✔️ 拓展知识仓✔️IOC是如何实现的&#xff1f; ✔️引言 所谓的IOC (inversion of control) &#xff0c;就是控制反转的意思。何为控制反转? 在传统的程序设计中&#xff0c;应用程序代码通常控制着对象的创建和…

Pycharm 切换interpreter---python的环境和第三方库问题

这篇回答两个问题&#xff1a; 1.为什么在 pycharm中打开新的project&#xff0c;切换interpreter 之后发现自己之前装的库消失了&#xff1f; 2.为什么 interpreter 切换到python3.8了&#xff0c; terminal 还是在 3.9&#xff1f;&#xff1f; 问题的关键&#xff1a;搞懂什…

STM32CubeMX学习(二) USB CDC 双向通信

STM32CubeMX学习&#xff08;二&#xff09; USB CDC 双向通信 简介CubeMX新建工程&#xff08;串口LED&#xff09;测试串口和LED串口接收测试USB CDC通信 简介 利用正点原子F407探索者开发板&#xff0c;测试基于USB CDC的双向数据通信。 CubeMX新建工程&#xff08;串口LE…

工业企业出口技术复杂度测算(2000-2014年)

工业企业出口技术复杂度的测算是对工业企业出口产品的技术含量和复杂度进行评估的过程。这种测算通常涉及分析出口产品的研发强度、生产过程的复杂性、所需的技术知识水平以及产品在全球市场上的竞争力。技术复杂度高的产品可能包括高端制造业产品&#xff0c;如先进电子设备、…

如何使用idea部署springboot项目全过程

博主介绍&#xff1a; ✌至今服务客户已经1000、专注于Java技术领域、项目定制、技术答疑、开发工具、毕业项目实战 ✌ &#x1f345; 文末获取源码联系 &#x1f345; &#x1f447;&#x1f3fb; 精彩专栏 推荐订阅 &#x1f447;&#x1f3fb; 不然下次找不到 Java项目精品实…

单机+内部备份_全备案例

此场景为单机数据库节点内部备份&#xff0c;方便部署和操作&#xff0c;但备份REPO与数据库实例处于同一个物理主机&#xff0c;冗余度较低。 前期准备 配置ksql免密登录(必须) 在Kingbase数据库运行维护中&#xff0c;经常用到ksql工具登录数据库&#xff0c;本地免密登录…

Unity | 快速修复Animation missing错误

目录 一、背景 二、效果 三、解决办法 一、背景 最近在做2D 骨骼动画相关的Demo&#xff0c;我自己使用Unity引擎进行骨骼绑定并创建了anim后&#xff0c;一切正常&#xff0c;anim也能播放。但是昨天我修改Obj及子物体的名称&#xff08;由中文改为英文&#xff0c;如&…

数据分析师,就是做报表?错!!

企业作为数据载体&#xff0c;没有数据&#xff0c;大概率也不会再有企业存在 !! 数据分析师&#xff0c;正是这只小舟的船桨&#xff0c;掌舵所有人的命运。注意&#xff0c;是分析师&#xff0c;不是表哥&#xff0c;表姐... 那么作为数据分析师&#xff0c;应该有哪些硬技能…

Python入门学习篇(十二)——内置函数匿名函数

1 内置函数——数学函数 1.1 绝对值函数 1.1.1 语法 abs(参数) # 里面的参数既可以是整数,也可以是小数1.1.2 示例代码 m -1.99 n -9 print(f"{m}的绝对值为: {abs(m)}") print(f"{n}的绝对值为: {abs(n)}")1.1.3 运行截图 1.2 求商和余数 1.2.1 语…

[Angular] 笔记 16:模板驱动表单 - 选择框与选项

油管视频&#xff1a; Select & Option (Template Driven Forms) Select & Option 在 pokemon.ts 中新增 interface: export interface Pokemon {id: number;name: string;type: string;isCool: boolean;isStylish: boolean;acceptTerms: boolean; }// new interface…

算法基础之蒙德里安的梦想

蒙德里安的梦想 核心思想&#xff1a; 状态压缩dp 总方案 横放的方案 剩下的地方竖着放是固定的了 状态压缩 &#xff1a; 将每一列的图(横终点 横起点 竖) 用一个二进制数存下 向后凸的为1 反之为0 状态计算&#xff1a; 所有 i – 1 列 不冲突的 都加和 f[i , j] f[i - 1…

图像文件怎么才能转换为Excel

将图像文件转换为Excel需要通过OCR&#xff08;Optical Character Recognition&#xff0c;光学字符识别&#xff09;技术&#xff0c;先将图片中的文字识别出来&#xff0c;再将识别出的文字导入到Excel中。这可以使用一些在线或离线的OCR工具&#xff0c;例如ABBYY FineReade…

Linux 线程安全 (2)

文章目录 线程同步概念条件变量使用生产消费模型信号量的使用读写锁的使用 Linux 线程安全 &#xff08;1&#xff09; 线程同步概念 竞态条件&#xff1a;因为时序问题&#xff0c;而导致程序异常. 饥饿问题&#xff1a;只使用互相锁保证线程安全时&#xff0c;锁资源总被某…

听说上海移动年终奖16个月!我承认我酸了!

* 你好&#xff0c;我是前端队长&#xff0c;在职场&#xff0c;玩副业&#xff0c;文末有福利! 今天&#xff0c;队长看到一篇帖子&#xff0c;有网友发帖说上海移动的年终奖发了16个月&#xff0c;我承认我酸了。 看到这里&#xff0c;我承认我也酸了。16个月是什么概念&…

案例-旋转的太极图案(HTML+CSS)

使用css的动画变换效果完成“ 旋转太极“。 <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title><style>*{margin: 0;padding: 0;background-color: antiquewhite;}.tj{width: 0;height: 300px;/* border…

CEC2017(Python):五种算法(PSO、RFO、SSA、DE、HHO)求解CEC2017

一、5种算法简介 1、粒子群优化算法PSO 2、红狐优化算法RFO 3、麻雀搜索算法SSA 4、差分进化算法DE 5、哈里斯鹰优化算法HHO 二、CEC2017简介 参考文献&#xff1a; [1]Awad, N. H., Ali, M. Z., Liang, J. J., Qu, B. Y., & Suganthan, P. N. (2016). “Problem de…

计算机网络【EPoll原理】

预备知识&#xff1a;内核poll钩子原理 内核函数poll_wait 把当前进程加入到驱动里自定义的等待队列上 &#xff1b; 当驱动事件就绪后&#xff0c;就可以在驱动里自定义的等待队列上唤醒调用poll的进程&#xff1b; 故poll_wait作用&#xff1a;可以让驱动知道事件就绪的时…

蛇目标检测数据集VOC格式100张

蛇是一种广泛分布于地球各个角落的爬行动物&#xff0c;是无脚类爬行动物中最为特殊的一类。它们身体长而细长&#xff0c;通常由许多鳞片组成&#xff0c;没有四肢。蛇生活的环境非常多样&#xff0c;可以在沙漠、森林、草原和水域等各种地方找到它们的踪迹。 蛇是以捕食其他…

VS2013中特殊操作

代码段管理器(查看代码补全快捷方式) 1.点击 工具 ->点击 代码片段管理器->看到 语言->选择 Visual C 2.可以点击下方添加 自定义一个属于自己的快捷代码补全方式 3.结果图&#xff1a; 设置自动换行与行号 1.点击 工具->点击 选项->找到 文本编辑器(然后点击)…

百度地图添加坐标点

​​​​​​html <!DOCTYPE html><html xmlns"http://www.w3.org/1999/xhtml"> <head runat"server"><meta http-equiv"Content-Type" content"text/html; charsetutf-8" /><title>查看签到信息-地图…