SpringBoot实战第五天

news2024/12/25 12:15:13

最后在开发一个文件上传接口,结束后端部分开发

文件上传接口

先看接口文档

阅读接口文档,唯一问题就是项目暂时还没有传到服务器上,所以对文件的存储与读取暂时在项目本地进行 

Controller层

@RestController
public class FileUploadController {
    @PostMapping("/upload")
    public Result<String> upload(MultipartFile file) throws IOException {
        //获取文件名
        String originalFilename = file.getOriginalFilename();
        //保证文件的名字是唯一的,防止文件覆盖
        String filename = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));
        //把文件内容存储到本地磁盘
        file.transferTo(new File("H:\\Desktop\\chessmanfile\\"+filename));
        return Result.success("rul地址");
    }
}

 

云存储头像

初见阿里OSS

下面使用阿里云OSS服务来存储头像

有关OSS存储的获取,bucket创建等部分掠过

由于我们写的项目是一个maven项目,根据官网的SDK引导,我们需要导入如下坐标

<!--        阿里云OSS依赖坐标-->
        <dependency>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
            <version>3.15.1</version>
        </dependency>

官网提示我们,使用1.9版本以上的JDK还需导入JAXB相关坐标

<!--        JAXB相关依赖-->
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.3.1</version>
        </dependency>
        <dependency>
            <groupId>javax.activation</groupId>
            <artifactId>activation</artifactId>
            <version>1.1.1</version>
        </dependency>
        <!-- no more than 2.3.3-->
        <dependency>
            <groupId>org.glassfish.jaxb</groupId>
            <artifactId>jaxb-runtime</artifactId>
            <version>2.3.3</version>
        </dependency>

 坐标导入完成后,我们使用官方给的一个示例文件进行测试

如上图,是从阿里云OSS官方示例文件修改而来的测试程序,其中修改了的部分都用下划线标出,第一个要修改的是endpoint,在控制台中可以找到

接下来就是accesskey的id和secret,官方文档给出的方法是将这两个信息配置进系统环境变量中去,这里不采用,直接定义两个变量来 ,再创建OSSClient实例中也作出了相应的修改

接下来就是本身的实例给服务器传入的是content,由于我们要存储的就是头像,所以直接改成传入一个图片来测试

运行了测试程序后在后台可以查看到我们的测试图片已经上传成功,下一步就是将图片的上传集成到我们的程序中去

云存储头像文件集成入程序 

我们书写一个工具类来使用,实际上就是根据上述修改方法对工具类进行仿写

public class AliOSSUtil {
    // Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。
    private static final String ENDPOINT = "https://oss-cn-beijing.aliyuncs.com";

    private static final String ACCESS_KEY_ID = "马赛克";
    private static final String ACCESS_KEY_SECRET = "马赛克";
    // 填写Bucket名称,例如examplebucket。
    private static final String BUCKET_NAME = "chessman";

    public static String main(String objectName , InputStream in) throws Exception {
        //初始化URL
        String URL = "";
        // 填写Object完整路径,完整路径中不能包含Bucket名称,例如exampledir/exampleobject.txt。


        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(ENDPOINT,ACCESS_KEY_ID,ACCESS_KEY_SECRET );

        try {
            // 填写字符串。
            String content = "Hello OSS,你好世界";

            // 创建PutObjectRequest对象。
            PutObjectRequest putObjectRequest = new PutObjectRequest(BUCKET_NAME, objectName, in);

            // 如果需要上传时设置存储类型和访问权限,请参考以下示例代码。
            // ObjectMetadata metadata = new ObjectMetadata();
            // metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard.toString());
            // metadata.setObjectAcl(CannedAccessControlList.Private);
            // putObjectRequest.setMetadata(metadata);

            // 上传字符串。
            PutObjectResult result = ossClient.putObject(putObjectRequest);
            URL = "https://"+BUCKET_NAME+"."+ENDPOINT_INDEX+"/"+ObjectName;
        } 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();
            }
        }
        return URL;
    }
}

在main方法的参数中我们传入了文件存储路径和一个字符输入流,对应的替换我们之前在测试时写死的两个地方

还有,我们之前在书写有关头像的接口时都是要获取其URL信息,所以我们要让该方法返回头像对应的URL信息

有关URL组成

https://chessman.oss-cn-beijing.aliyuncs.com/111.jpg

这是刚刚上传的111.jpg的URL,可以看出,URL的组成为

https://BucketName.Endpoint/ObjectName

这一格式

所以我们可以通过字符串的拼接来实现URL的返回

下面就是对刚刚写的文件上传接口进行修改

文件上传接口修改

@RestController
public class FileUploadController {
    @PostMapping("/upload")
    public Result<String> upload(MultipartFile file) throws Exception {
        //获取文件名
        String originalFilename = file.getOriginalFilename();
        //保证文件的名字是唯一的,防止文件覆盖
        String filename = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));
        //把文件内容存储到本地磁盘
        //file.transferTo(new File("H:\\Desktop\\chessmanfile\\"+filename));
        String url = AliOSSUtil.uploadFile(filename,file.getInputStream());
        return Result.success(url);
    }
}

至此,后端的所有接口全部构建完毕!!!!!!!!!

完结撒花!

redis登录接口优化

当然,接口书写完毕不代表程序的维护和健壮性增强也到此为止,下面就用redis对登录接口进行优化

问题提出:我们对于用户的身份验证是依靠令牌的识别,但是我们令牌只有一个24小时过期机制,当用户更新了他的密码之后,会生成一个新的令牌,但此时旧令牌没有被我们回收,仍然能使用,我们需要有令牌主动失效机制

令牌主动失效机制

登陆成功后,给浏览器相应令牌的同时,给浏览器相应令牌的同时,把该令牌存储到redis中

LoginInterceptor拦截器中,需要验证浏览器携带的令牌,并同时需要获取到redis中存储的与之相同的令牌

当用户修改密码成功后,删除redis中存储的旧令牌

SpirngBoot集成redis

导入spring-boot-starter-data-redis起步依赖

<!--        redis坐标-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

在yml配置文件中,配置redis连接信息

  data:
    redis:
      host: localhost
      port: 6379

调用API(StringRedisTemplate)完成字符串的存取操作

以两个测试方法为例

    @Test
    public void testSet(){
        //redis中存储键值对 StringRedisTemplate
        ValueOperations<String, String> Operations = stringRedisTemplate.opsForValue();
        Operations.set("username","cacb");
        //设置键值对及其过期时间
        Operations.set("timetest","haha" ,15, TimeUnit.SECONDS);
    }

    @Test
    public void tsetGet(){
        //从redis中获取键值对
        ValueOperations<String, String> Operations = stringRedisTemplate.opsForValue();
        System.out.println(Operations.get("username"));
    }

令牌主动失效代码实现

第一步、修改登录接口代码

            //将token存入到redis中
            ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
            operations.set(token,token,12, TimeUnit.HOURS);
            return Result.success(token);

可以看到,修改就是在成功登录后向页面返回token的同时将token存入到redis中去,这里我设置的键值对过期时间与token过期时间一致 

第二步、修改修改密码接口代码

        userService.updatePwd( newPwd);
        //删除redis中对应的token
        ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
        operations.getOperations().delete(token);

        return Result.success();

可以看到,在成功玩修改了密码之后,要删除掉redis中的旧token,这里token从请求头中获取 

 public Result updatePwd(@RequestBody @Valid Map<String,String> params,@RequestHeader("Authorization") String token)

所以要对修改密码接口的传参进行如上修改 

第三步、修该登录拦截器代码

            //从redis中获取相同的的token
            ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
            String redis_token = operations.get(token);
            if(redis_token == null){
                //此时token已经失效
                throw new RuntimeException();
            }

可以看到,在拦截其中我们将浏览器中获取的token作为键来从redis中获取值,如果查询到的值为空,那么就证明浏览器给出的token已经失效了,所以这时候就要拦截浏览。

至此,redis完善登录相关接口工作就基本完毕了

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

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

相关文章

探索设计模式的魅力:揭秘模版方法模式-让你的代码既灵活又可维护

设计模式专栏&#xff1a;http://t.csdnimg.cn/U54zu 目录 一、开篇二、应用场景一坨坨代码实现存在的问题 三、解决方案模式方法结构示意图及说明用模板方法模式重构示例解决的问题 四、工作原理使用模板方法模式重写示例结构图核心结构&#xff1a;抽象类和具体实现 五、总结…

IDEA 的28 个天花板技巧,yyds!

IDEA 作为Java开发工具的后起之秀,几乎以碾压之势把其他对手甩在了身后,主要原因还是归功于:好用;虽然有点重,但依旧瑕不掩瑜,内置了非常多的功能,大大提高了日常的开发效率,下面汇总了常用的28个使用小技巧,学会之后,让你的撸码效率直接起飞... 注意:不同idea版本菜…

计算机二级之sql语言的学习(数据模型—概念模型)

概念模型 含义: 概念模型用于信息世界&#xff08;作用对象&#xff09;的建模&#xff0c;是实现现实世界到信息世界&#xff08;所以万丈高楼平地起&#xff0c;不断地学习相关的基础知识&#xff0c;保持不断地重复才能掌握最为基础的基础知识&#xff09;的概念抽象&#…

SG5032VEN晶体振荡器SPXO

在高速数字通信和精密电子系统中&#xff0c;时钟信号的质量至关重要。SG5032VEN晶体振荡器&#xff08;SPXO&#xff09;凭借其低相位抖动的LVDS输出&#xff0c;为这些应用提供了理想选择。提供频率范围:200.1 MHz ~ 500mhz&#xff0c;满足了从高速网络到数据中心等不同应用…

Java编程在工资信息管理中的最佳实践

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

【c++】析构函数

1.特征 析构函数是特殊的成员函数&#xff0c;其特征如下&#xff1a; 1.析构函数名是在类名前加上字符~。 2.无参数无返回值类型。 3.一个类只能有一个析构函数。若未显式定义&#xff0c;系统会自动生成默认的析构函数。注意&#xff1a;析构函数不能重载。 4.对象生命周…

HTML-多媒体嵌入-MDN文档学习笔记

HTML-多媒体与嵌入 查看更多学习笔记&#xff1a;GitHub&#xff1a;LoveEmiliaForever MDN中文官网 HTML-中的图片 将图片放入网页 可以使用<img/>来将图片嵌入网页&#xff0c;它是一个空元素&#xff0c;最少只需src属性即可工作 <img src"图片链接"…

【Python】测量WAV文件播放时长

问题 windows播放WAV音频文件&#xff0c;一般使用API函数&#xff0c;如PlaySound。实际使用发现&#xff0c;从调用PlaySound到实际开始播放存在200ms以上的延时&#xff0c;在游戏编程中音效实时性是个需要解决的问题。 本文主要讨论&#xff0c;windows播放WAV文件的衍生…

JVM-JVM中对象的生命周期

申明&#xff1a;文章内容是本人学习极客时间课程所写&#xff0c;文字和图片基本来源于课程资料&#xff0c;在某些地方会插入一点自己的理解&#xff0c;未用于商业用途&#xff0c;侵删。 原资料地址&#xff1a;课程资料 对象的创建 常量池检查:检查new指令是否能在常量池…

ESP32-Cam学习(1)——拍摄第一张照片

1.开发板介绍 使用的ESP32-Cam实物图为&#xff1a; 在某宝可以轻易买到。它分为主板&#xff0c;和底板。底板的主要功能是供电、程序下载等等。主板才是ESP32芯片的核心。 2.固件烧录 使用摄像头之前&#xff0c;需要给ESP32刷入支持摄像头的固件库&#xff0c;其下载地址为…

(07)Hive——窗口函数详解

一、 窗口函数知识点 1.1 窗户函数的定义 窗口函数可以拆分为【窗口函数】。窗口函数官网指路&#xff1a; LanguageManual WindowingAndAnalytics - Apache Hive - Apache Software Foundationhttps://cwiki.apache.org/confluence/display/Hive/LanguageManual%20Windowing…

线索化二叉树(先序,中序,后序)+线索化二叉树的遍历【java详解】

目录 线索化二叉树的基本介绍&#xff1a; 举个栗子&#xff1a; 二叉树的中序线索化&#xff1a; 创建HeroNode类&#xff0c;表示节点信息&#xff1a; 编写中序线索化方法代码&#xff1a; 中序线索化遍历代码&#xff1a; 测试代码&#xff1a; 测试结果&#xff1a…

OpenAI发布Sora技术报告深度解读!真的太强了!

&#x1f60e; 作者介绍&#xff1a;我是程序员洲洲&#xff0c;一个热爱写作的非著名程序员。CSDN全栈优质领域创作者、华为云博客社区云享专家、阿里云博客社区专家博主、前后端开发、人工智能研究生。公粽号&#xff1a;洲与AI。 &#x1f388; 本文专栏&#xff1a;本文收录…

NodeJS背后的人:Express

NodeJS背后的人&#xff1a;Express 本篇文章&#xff0c;学习记录于&#xff1a;尚硅谷&#x1f3a2; 文章简单学习总结&#xff1a;如有错误 大佬 &#x1f449;点. 前置知识&#xff1a;需要掌握了解&#xff1a; JavaScript基础语法 、Node.JS环境API 、前端工程\模块化 …

代码随想录算法训练营第53天 | 121.买卖股票的最佳时机 + 122.买卖股票的最佳时机II

今日任务 121. 买卖股票的最佳时机 122.买卖股票的最佳时机II 121.买卖股票的最佳时机 - Easy 题目链接&#xff1a;力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 给定一个数组 prices &#xff0c;它的第 i 个元素 prices[i] 表示一支给定股票第…

Rust 语言学习杂谈 (end) (各种工作中遇到的疑难杂症)

1.在运行 “cargo build --release” 的时候&#xff0c;到底发生了什么&#xff1f; 源 (GPT4.0) : 当我们运行 cargo build --release 命令时&#xff0c;实际上在进行一系列复杂的步骤来编译和构建 Rust 项目的发布版本。这个过程大致可以分解为以下几个步骤&#xff1a;…

Java - SPI机制

本文参考&#xff1a;SPI机制 SPI&#xff08;Service Provider Interface&#xff09;&#xff0c;是JDK内置的一种服务提供发现机制&#xff0c;可以用来启动框架扩展和替换组件&#xff0c;主要是被框架的开发人员使用&#xff0c;比如 java.sql.Driver接口&#xff0c;其他…

【C语言】实现队列

目录 &#xff08;一&#xff09;队列 &#xff08;二&#xff09;头文件 &#xff08;三&#xff09; 功能实现 &#xff08;1&#xff09;初始化 &#xff08;2&#xff09; 销毁队列 &#xff08;3&#xff09; 入队 &#xff08;4&#xff09;出队 &#xff08;5&a…

【论文精读】GPT1

摘要 如何从大量未标注文本中获取词级别的信息有两个主要挑战&#xff0c;使用何种优化目标能有效地学习文本表示&#xff0c;如何有效地将学习到的表示迁移到目标任务。针对这些问题&#xff0c;本文提出一种无监督预训练和有监督微调的组合的半监督方法&#xff0c;具体为&am…

Vue3+Ant-Design-Vue:报错Cannot read properties of null (reading ‘isCE‘)

问题描述 在使用Ant-Design-Vue内置的Table表格组件&#xff0c;实现expand展开行功能时&#xff0c;报错&#xff1a;Uncaught TypeError: Cannot read properties of null (reading ‘isCE‘) 。 报错信息图示&#xff1a; 在GitHub上找到如下描述&#xff0c; 解决方案 网上…