• 直播课堂系统08-腾讯云对象存储和课程分类管理


    开通对象存储

    腾讯云开通对象存储COS服务

    在这里插入图片描述

    创建存储桶

    记得选择公有读写
    在这里插入图片描述

    测试上传

    点击桶名称,进入详情页,可以测试上传文件
    在这里插入图片描述

    创建API密钥

    在API密钥管理里面新建一个自己的密钥,就不截图了。

    引入依赖

    <dependency>
           <groupId>com.qcloud</groupId>
           <artifactId>cos_api</artifactId>
           <version>5.6.54</version>
    </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    测试代码

    import com.alibaba.fastjson.JSON;
    import com.qcloud.cos.COSClient;
    import com.qcloud.cos.ClientConfig;
    import com.qcloud.cos.auth.BasicCOSCredentials;
    import com.qcloud.cos.http.HttpProtocol;
    import com.qcloud.cos.model.PutObjectRequest;
    import com.qcloud.cos.model.PutObjectResult;
    import com.qcloud.cos.region.Region;
    
    import java.io.File;
    
    public class FileTest {
        public static void main(String[] args) {
            //初始化用户信息secretId secretKey
            String secredId = "你的id";
            String secretKey = "你的密钥";
            //创建验证
            BasicCOSCredentials cred = new BasicCOSCredentials(secredId, secretKey);
            //设置bucket的地域
            Region region = new Region("ap-nanjing");
            // clientConfig 中包含了设置 region, https(默认 http), 超时,
            // 代理等 set 方法, 使用可参见源码或者常见问题 Java SDK 部分。
            ClientConfig clientConfig = new ClientConfig(region);
            //初始化 默认使用https
            clientConfig.setHttpProtocol(HttpProtocol.https);
            //生成cos客户端
            COSClient cosClient = new COSClient(cred, clientConfig);
            try{
                //指定上传的文件
                File file = new File("文件地址\\111.png");
                //指定文件将要存放的存储桶
                String bucketName = "桶名称";
                // 指定文件上传到 COS 上的路径,即对象键。
                // 例如对象键为folder/picture.jpg
                // 则表示将文件 picture.jpg 上传到 folder 路径下
                String key = "test-11.png";
                PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName,key,file);
                PutObjectResult putObjectResult = cosClient.putObject(putObjectRequest);
                System.out.println(JSON.toJSONString(putObjectResult));
            }catch (Exception clientException){
                clientException.printStackTrace();
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    整合腾讯云对象存储

    service_vod模块引入依赖

            <dependency>
                <groupId>com.qcloudgroupId>
                <artifactId>cos_apiartifactId>
                <version>5.6.54version>
            dependency>
    
            
            <dependency>
                <groupId>joda-timegroupId>
                <artifactId>joda-timeartifactId>
            dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    修改本地配置

    在application.properties里添加如下内容

    spring.servlet.multipart.max-file-size=1024MB
    spring.servlet.multipart.max-request-size=1024MB
    #不同的服务器,地址不同
    tencent.cos.file.region=ap-nanjing
    tencent.cos.file.secretid=你的id
    tencent.cos.file.secretkey=你的key
    #bucket可以在控制台创建,也可以使用java代码创建
    tencent.cos.file.bucketname=你的bucketName
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    创建工具类

    该工具类为常量类,专门用来读取properties里的配置。
    在vod里面创建utils文件夹并且创建如下java类
    当一个类实现InitialzingBean接口之后,初始化Bean时,若该Bean实现InitialzingBean接口,会自动调用afterPropertiesSet()方法,完成一些用户自定义的初始化操作。

    package vod.utils;
    
    import org.springframework.beans.factory.InitializingBean;
    import org.springframework.beans.factory.annotation.Value;
    
    public class ConstantPropertiesUtil implements InitializingBean {
        @Value("${tencent.cos.file.region}")
        private String region;
        @Value("${tencent.cos.file.secretid}")
        private String secretId;
        @Value("${tencent.cos.file.secretkey}")
        private String secretKey;
        @Value("${tencent.cos.file.bucketname}")
        private String bucketName;
    
        public static String END_POINT;
        public static String ACCESS_KEY_ID;
        public static String ACCESS_KEY_SECRET;
        public static String BUCKET_NAME;
    
        @Override
        public void afterPropertiesSet() throws Exception {
            END_POINT = region;
            ACCESS_KEY_ID = secretId;
            ACCESS_KEY_SECRET = secretKey;
            BUCKET_NAME = bucketName;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    创建Service

    创建对应的接口,且声明方法

    package vod.service;
    
    import org.springframework.web.multipart.MultipartFile;
    
    public interface FileService {
        //文件上传
        String upload(MultipartFile file);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    impl实现service

    package vod.service.impl;
    
    import com.alibaba.fastjson.JSON;
    import com.qcloud.cos.COSClient;
    import com.qcloud.cos.ClientConfig;
    import com.qcloud.cos.auth.BasicCOSCredentials;
    import com.qcloud.cos.auth.COSCredentials;
    import com.qcloud.cos.http.HttpProtocol;
    import com.qcloud.cos.model.ObjectMetadata;
    import com.qcloud.cos.model.PutObjectRequest;
    import com.qcloud.cos.model.PutObjectResult;
    import com.qcloud.cos.region.Region;
    import org.joda.time.DateTime;
    import org.springframework.stereotype.Service;
    import org.springframework.web.multipart.MultipartFile;
    import vod.service.FileService;
    import vod.utils.ConstantPropertiesUtil;
    import java.util.UUID;
    import java.io.InputStream;
    
    @Service
    public class FileServiceImpl implements FileService {
        @Override
        public String upload(MultipartFile file) {
            String endPoint = ConstantPropertiesUtil.END_POINT;
            String bucketName = ConstantPropertiesUtil.BUCKET_NAME;
            //初始化用户信息
            String accessKeyId = ConstantPropertiesUtil.ACCESS_KEY_ID;
            String accessKeySecret = ConstantPropertiesUtil.ACCESS_KEY_SECRET;
            //创建验证
            COSCredentials cred = new BasicCOSCredentials(accessKeyId, accessKeySecret);
            // 2 设置 bucket 的地域
            // clientConfig 中包含了设置 region, https(默认 http),超时, 代理等 set 方法
            Region region = new Region(ConstantPropertiesUtil.END_POINT);
            ClientConfig clientConfig = new ClientConfig(region);
            // 从 5.6.54 版本开始,默认使用了 https
            clientConfig.setHttpProtocol(HttpProtocol.https);
            // 3 生成 cos 客户端。
            COSClient cosClient = new COSClient(cred, clientConfig);
            try{
                // 指定要上传的文件
                InputStream inputStream = file.getInputStream();
                // 指定文件将要存放的存储桶
                // 指定文件上传到 COS 上的路径,即对象键。例如对象键为folder/picture.jpg,则表示将文件 picture.jpg 上传到 folder 路径下
                //构建出一个key 加上时间日期
                String key = UUID.randomUUID().toString().replaceAll("-","")+
                        file.getOriginalFilename();
                String dateUrl = new DateTime().toString("yyyy/MM/dd");
                key = dateUrl+"/"+key;
                //上传的文件
                ObjectMetadata objectMetadata = new ObjectMetadata();
                PutObjectRequest putObjectRequest =
                        new PutObjectRequest(bucketName, key, inputStream,objectMetadata);
                //将其上传 查看结果
                PutObjectResult putObjectResult = cosClient.putObject(putObjectRequest);
                System.out.println(JSON.toJSONString(putObjectResult));
                //生成的路径/文件如下所示 将路径返回
                //https://ggkt-atguigu-1310644373.cos.ap-beijing.myqcloud.com/01.jpg
                String url = "https://"+bucketName+"."+"cos"+"."+endPoint+".myqcloud.com"+"/"+key;
                return url;
            } catch (Exception clientException) {
                clientException.printStackTrace();
                return null;
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67

    创建Controller

    新建一个FileUploadController,注意声明的 @ApiParam(name = “file”, value = “文件”, required = true)没有实际作用,是给swagger注解的,重要的是@RequestParam

    @Api(tags = "文件上传接口")
    @RestController
    @CrossOrigin
    @RequestMapping("/admin/vod/file")
    public class FileUploadController {
        @Autowired
        private FileService fileService;
    
        //文件上传
        @ApiOperation(value = "文件上传")
        @PostMapping("upload")
        public Result upload(
                @ApiParam(name="file",value = "文件",required = true)
                @RequestParam("file") MultipartFile file){
            String uploadUrl = fileService.upload(file);
            //成功的url给前端展示
            String upload = fileService.upload(file);
            return Result.ok(uploadUrl).message("文件上传成功");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    添加讲师前端完善(上传讲师头像)

    进入form.vue,之前写在这块地方
    在这里插入图片描述

    改成

          <el-form-item label="讲师头像">
            <el-upload
              :show-file-list="false"
              :on-success="handleAvatarSuccess"
              :before-upload="beforeAvatarUpload"
              :on-error="handleAvatarError"
              :action="BASE_API+'/admin/vod/file/upload?module=avatar'"
              class="avatar-uploader">
              <img v-if="teacher.avatar" :src="teacher.avatar">
              <i v-else class="el-icon-plus avatar-uploader-icon"/>
            </el-upload>
          </el-form-item>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    注意这里的action是直接访问了后台接口,把上传的avatar送了过去,其中还写了三个方法:创建前,成功时,错误时。
    在data数据模型定义中加上:

          BASE_API: 'http://localhost:8301',//统一开头
    
    • 1

    添加上传操作方法

    之前头像已经定义过,所以直接赋值即可。
    在这里插入图片描述

        handleAvatarSuccess(res,file){
          //上传成功时后端代码会返回code和图片文件url(已经存到云服务器上)
          if(res.code==200){
            this.teacher.avatar = res.data
            //强制重新渲染
            this.$forceUpdate()
          }else{
            this.$message.error('上传失败(非0)')
          }
        },
        //错误处理
        handleAvatarError(){
          console.log('error')
          this.$message.error('上传失败(http失败)')
        },
        //上传校验
        beforeAvatarUpload(file) {
          const isJPG = file.type === 'image/jpeg'
          const isLt2M = file.size / 1024 / 1024 < 2
          if (!isJPG) {
            this.$message.error('上传头像图片只能是 JPG 格式!')
          }
          if (!isLt2M) {
            this.$message.error('上传头像图片大小不能超过 2MB!')
          }
          return isJPG && isLt2M
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    疑问

    我有个问题,上传的时候没见把avatar存到数据库里啊,那么下次再来看的时候不就不会显示了吗…
    答:想起来了,之前写保存老师的时候,已经把avatar的内容写在里面了,所以不用再写一遍。

    课程分类管理模块

    需求:分类列表

    在这里插入图片描述

    需求:导入导出

    在这里插入图片描述
    在这里插入图片描述

    数据库设计

    数据库在glkt_vod的subject里
    在这里插入图片描述
    各种课类型的名称,其父类课程,排序,创建时间和更新时间。

    课程分类列表

    课程分类采用树形展示,考虑使用“树形数据与懒加载”的方式展现数据列表,可以先看一下提示。
    懒加载的意思是点击一层出现一层,初始只有一层。
    在这里插入图片描述
    在这里插入图片描述
    进去看代码,首先返回的这些字段要和html的对应
    在这里插入图片描述
    其次懒加载时会调用load方法,在有孩子地方设置hasChildren
    在这里插入图片描述
    它是根据有没有孩子来确定是否有子节点的,所以我们需要返回指定的行是否包含子节点。

    编写SubjectMapper

    public interface SubjectMapper extends BaseMapper<Subject> {
    }
    
    
    • 1
    • 2
    • 3

    编写SubjectService

    实现其接口

    public interface SubjectService extends IService<Subject> {
        //查询下一层课程分类
        List<Subject> findChildSubject(Long id);
    }
    
    • 1
    • 2
    • 3
    • 4

    impl实现

    这里面有个问题,查找用的是baseMapper实现的,按道理来说应该用SubjectMapper实现,于是决定改一下。

    @Service
    public class SubjectServiceImpl extends ServiceImpl<SubjectMapper, Subject> implements SubjectService {
        //声明mapper
        @Autowired
        private SubjectMapper mapper;
    
        //找到下一层的课程分类
        @Override
        public List<Subject> findChildSubject(Long id) {
            //首先进数据库查询
            QueryWrapper<Subject> subjectQueryWrapper = new QueryWrapper<>();
            //找到parent_id为id的课程
            subjectQueryWrapper.eq("parent_id",id);
            //将这些list提出来
            List<Subject> subjects = mapper.selectList(subjectQueryWrapper);
            //设置他们是否有孩子
            for(Subject subject:subjects){
                Long sid = subject.getId();
                boolean childen = this.isChilden(sid);
                //这里时数据库不存的 所以model上声明了了exist=false
                subject.setHasChildren(childen);
            }
            return subjects;
        }
        //判断id下面是否有子节点 即该课程是否为父节点
        private boolean isChilden(Long id){
            QueryWrapper<Subject> subjectQueryWrapper = new QueryWrapper<>();
            subjectQueryWrapper.eq("parent_id",id);
            Integer count = mapper.selectCount(subjectQueryWrapper);
            return count>0;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    编写controller

    @Api(tags = "课程分类管理")
    @RestController
    @RequestMapping(value="/admin/vod/subject")
    //@CrossOrigin
    public class SubjectController {
        @Autowired
        private SubjectService subjectService;
        //查询下一层课程分类
        //根据parent_id
        @ApiOperation("查询下一层的课程分类")
        @GetMapping("getChildSubject/{id}")
        public Result getChildSubject(@PathVariable Long id) {
            List<Subject> list = subjectService.selectList(id);
            return Result.ok(list);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    课程分类列表前端

    添加路由

    在router/index.js里添加路由

      //课程分类列表
      {
        path:'/subject',
        component:Layout,
        redirect: '/subject/list',
        name:'课程分类管理',
        alwaysShow:true,
        meta: {title: '课程分类管理',icon:'example'},
        children:[
          {
            path:'list',
            name:'课程分类列表',
            component:() => import('@/views/vod/subject/list'),
            meta:{title:'课程分类列表',iconL:'table'}
          }
        ]
      },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    定义数据字典列表接口

    创建src/api/vod/subject.js

    import request from '@/utils/request'
    //对api_name传入方法时 即往后端发送请求
    const api_name = '/admin/vod/subject'
    export default{
        getChildList(id){
            return request({
                url:`${api_name}/getChildSubject/{id}`,
                method:'get'
            })
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    编写subject/list.vue

    <template>
        <div  class="app-container">
        <el-table
            :data="list"
            style="width: 100%"
            row-key="id"
            border
            lazy
            :load="load"
            :tree-props="{children: 'children', hasChildren: 'hasChildren'}">
            <el-table-column
            prop="title"
            label="名称"
            width="150">
            </el-table-column>
            <el-table-column
            prop="createTime"
            label="创建时间">
            </el-table-column>
        </el-table>
        </div>
    </template>
    <script>
    
    import subjectApi from '@/api/vod/subject'
    export default {
        data() {
            return {
                list:[] //数据字典列表数组
            }
        },
        created() {
          //就查第一层的意思
          this.getSubList(0)
        },
        methods: {
            //数据字典列表
            getSubList(id) {
                subjectApi.getChildList(id)
                    .then(response => {
                        this.list = response.data
                    })
            },
            //展示下层数据
            load(tree, treeNode, resolve) {
                subjectApi.getChildList(tree.id).then(response => {
                    resolve(response.data)
                })
          }
        }
    }
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    效果

    在这里插入图片描述

  • 相关阅读:
    【C++】智能指针
    《C++新经典》第16章 智能指针
    JMeter+influxdb+grafana性能测试监控平台
    智慧党务管理源码,竞赛答题+阅读学习一套系统全搞定
    excel单元格数字拆分比较
    dropbear-ssh2
    365天深度学习训练营-第3周:天气识别
    Java中的抽象类与接口介绍
    Go 1.23中的自定义迭代器与iter包
    音视频关键技术盘点!小白入行指南
  • 原文地址:https://blog.csdn.net/z754916067/article/details/126231609