谷粒学院day09——课程发布与阿里云视频点播服务

2023-10-27

image-20220113200536326

1.课程信息确认
1.1 后端实现

image-20220111191850725

到目前为止,我们已经完成了上图的第1,2步,现在开始做第三步的功能:课程最终发布。要实现课程的最终发布,我们需要让用户确认添加的各个信息,因此我们需要先把这些信息查询到。要查询的数据包括:课程名称、课程价格、课程简介…这显然涉及到多张表。

设计到查询多张表的内容,我们一般有两种思路:

  • 封装PO类(适用表的数量较少的情况,之前就是这么处理的)
  • 编写复杂sql、

显然,咱们涉及的表的数量较多,编写sql语句进行多表查询。首先课程表的数据我们全部需要,另外如果有其它课程信息也需要查询,可以使用左外连接进行。

sql语句如下,注意where子句中的ec.id替换成自己的数据库中存在的id。

SELECT ec.id,ec.title,ec.price,ec.lesson_num,
			 ecd.description,
			 et.name,
			 es1.title AS oneSubject,
			 es2.title AS twoSubject
FROM edu_course ec
LEFT JOIN edu_course_description ecd ON ec.id=ecd.id
LEFT JOIN edu_teacher et ON ec.teacher_id=et.id
LEFT JOIN edu_subject es1 ON ec.subject_parent_id=es1.id
LEFT JOIN edu_subject es2 ON ec.subject_id=es2.id
WHERE ec.id='1'

接下来我们在com.wangzhou.eduservice.entity.vo下定义CoursePublishVO

@Data
public class CoursePublishVo implements Serializable {

    private static final long serialVersionUID = 1L;

    private String id;//课程id
    
    private String title; //课程名称

    private String cover; //封面

    private Integer lessonNum;//课时数

    private String subjectLevelOne;//一级分类

    private String subjectLevelTwo;//二级分类

    private String teacherName;//讲师名称

    private String price;//价格 ,只用于显示

}

com.wangzhou.eduservice.mapper。

public interface EduCourseMapper extends BaseMapper<EduCourse> {
    public CoursePublishVO getPublishCourseInfo(String courseId);
}

com.wangzhou.eduservice.mapper.xml.EduCourseMapper.xml。请读者特别注意sql语句的变量命名要与PO类中的变量名保持一致。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wangzhou.eduservice.mapper.EduCourseMapper">
     <select id="getPublishCourseInfo" resultType="com.wangzhou.eduservice.entity.vo.CoursePublishVO">
        SELECT ec.id,ec.title,ec.price,ec.lesson_num,ec.cover,
               ecd.description,
               et.name AS teacher_name,
               es1.title AS subject_level_one,
               es2.title AS subject_level_two
        FROM edu_course ec
                 LEFT JOIN edu_course_description ecd ON ec.id=ecd.id
                 LEFT JOIN edu_teacher et ON ec.teacher_id=et.id
                 LEFT JOIN edu_subject es1 ON ec.subject_parent_id=es1.id
                 LEFT JOIN edu_subject es2 ON ec.subject_id=es2.id
        WHERE ec.id=#{id}
    </select>
</mapper>

现在mapper已经完成了,我们来实现调用吧。

com.wangzhou.eduservice.controller.EduCourseController.

//根据课程id查询课程确认信息
@GetMapping("/getpublishCourseInfo/{id}")
public R getpublishCourseInfo(@PathVariable String id){
    CoursePublishVO publishCourseInfo = eduCourseService.getPublishCourseInfo(id);
    return R.ok().data("publishCourse",publishCourseInfo);
}

EduCourseServiceImpl

  @Override
    public CoursePublishVO getPublishCourseInfo(String id) {
        return baseMapper.getPublishCourseInfo(id);

    }

至此,就完成了后端的接口部分,读者可以使用swagger自测。

出了点问题。

image-20220113201420335

image-20220113201606605

看起来是因为找不到mapper文件。

这是由于maven的默认加载机制,只会把src/main/java下的java文件加载,该目录下其它文件不会加载。而我们的xml文件放在下图位置,显然是不会被加载的。

image-20220114192538291

看看文件的输出目录。果然没有加载这些xml文件。

image-20220114192817858

解决方法有三种:

(1)直接手动复制

(2)通过更改配置文件方式使maven加载

可以更改pom.xml和application.properties,为了使service下的子模块都能够生效,我们更改service下的pom文件。

 <build>
        <resources>
            <resource>
                <directory>/src/main/java</directory>
                <includes>
                    <!-- **表示多层目录 -->
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
</build>

注:加在depencies后面。

image-20220114194430040

注:classpath就是指src下的路径,或者说是tagret下的classes路径(参考下图),注意改成自己的路径。

image-20220114194549842

重启项目,测试通过。(注:如果文件没有被复制过去手动拷贝吧)

image-20220114200246493

1.2 前端实现

现在后端部分已经完成了,接下来要实现前端部分,首先我们要从课程信息的页面跳转到信息确认的页面,这个功能之前在

chapter.vue的next()方法中已经实现了。

image-20220117195502103

在publish.vue页面需要做两件事情:从路由中得到courseId,调用接口显示数据。

(1)先实现前端的接口course.vue。

 // 根据id查询课程确认信息
    getpublishCourseInfo(id){
      return request({
          url:`/eduservice/edu-course/getpublishCourseInfo/${id}`,
          method: 'get',
      })
  }

(2)从路由中拿到courseId

publish.vue

image-20220117201426560

 created() {
    if(this.$route.params && this.$route.params.id) {
      this.courseid = this.$route.params.id
    }
  }

(3)调接口

import publishCourse from "@/api/edu/course.js";

image-20220117202013601

  getpublishCourseInfo(){
      publishCourse.getpublishCourseInfo(this.courseid)
      .then(resp => {
        this.publishCourse = resp.data.publishCourse
      })
    }

(4)显示数据

image-20220117202737681

(5)样式

加亿点点样式。

<template>
  <div class="app-container">
    <h2 style="text-align: center">发布新课程</h2>
    <el-steps
      :active="3"
      process-status="wait"
      align-center
      style="margin-
bottom: 40px;"
    >
      <el-step title="填写课程基本信息" />
      <el-step title="创建课程大纲" />
      <el-step title="最终发布" />
    </el-steps>
    <div class="ccInfo">
      <img :src="publishCourse.cover" />
      <div class="main">
        <h2>{{ publishCourse.title }}</h2>
        <p class="gray">
          <span>{{ publishCourse.lessonNum }}课时</span>
        </p>
        <p>
          <span
            >所属分类:{{ publishCourse.subjectLevelOne }}{{ publishCourse.subjectLevelTwo }}</span
          >
        </p>
        <p>课程讲师:{{ publishCourse.teacherName }}</p>
        <h3 class="red">{{ publishCourse.price }}</h3>
      </div>
    </div>

    <el-form label-width="120px">
      <el-form-item>
        <el-button @click="previous">返回修改</el-button>
        <el-button :disabled="saveBtnDisabled" type="primary" @click="publish"
          >发布课程</el-button
        >
      </el-form-item>
    </el-form>
  </div>
</template>
<style scoped>
.ccInfo {
  background: #f5f5f5;
  padding: 20px;
  overflow: hidden;
  border: 1px dashed #ddd;
  margin-bottom: 40px;
  position: relative;
}
.ccInfo img {
  background: #d6d6d6;
  width: 500px;
  height: 278px;
  display: block;
  float: left;
  border: none;
}
.ccInfo .main {
  margin-left: 520px;
}
.ccInfo .main h2 {
  font-size: 28px;
  margin-bottom: 30px;
  line-height: 1;
  font-weight: normal;
}
.ccInfo .main p {
  margin-bottom: 10px;
  word-wrap: break-word;
  line-height: 24px;
  max-height: 48px;
  overflow: hidden;
}
.ccInfo .main p {
  margin-bottom: 10px;
  word-wrap: break-word;
  line-height: 24px;
  max-height: 48px;
  overflow: hidden;
}
.ccInfo .main h3 {
  left: 540px;
  bottom: 20px;
  line-height: 1;
  font-size: 28px;
  color: #d32f24;
  font-weight: normal;
  position: absolute;
}
</style>

课程确认信息的显示就实现了。测试如下。

image-20220117220652827

2.课程的最终发布

现在课程虽然已经添加到了数据库了,但是课程还没有真正的发布:只有在课程发布以后用户才可以看到课程。在数据库中有一个字段status,发布的课程会将其置成Normal

image-20220118203219213

因此,所谓的课程发布其实是修改操作,我们可以直接调用之前的updateCourseInfo()接口,不过为了使功能更加清晰,我们重新在

EduCourseController中实现publishCourse方法。

     // 发布课程
    // 修改课程状态
    @PostMapping("/publishCourseInfo/{courseId}")
    public R publishCourseInfo(@PathVariable String courseId) {
        EduCourse course = new EduCourse();
        course.setId(courseId);
        course.setStatus("Normal");
        eduCourseService.updateById(course);
        return R.ok();
    }

course.js

  //发布课程
   publishCourseInfo(courseId){
    return request({
        url:`/eduservice/edu-course/publishCourseInfo/${courseId}`,
        method: 'post'
    })
  }

publish.vue。

 publish(){
      publishCourse.publishCourseInfo(this.courseid)
      .then(resp => {
         // 提示课程发布成功
          //提示信息
          this.$message({
            type: "success",
            message: "发布成功!",
          });
         // 页面跳转
         this.$router.push({ path: "/course/list" });
      })
     
    }
3.课程列表功能

需求如下图。与之前的讲师列表内容基本是一致的。

image-20220118212313674

先来实现最简单的部分:数据的展示。EduCourseController类。

   // 课程列表
    // Todo 完善成条件查询带分页功能
    @GetMapping("/getCourseList")
    public R getCourseList() {
        List<EduCourse> courseList = eduCourseService.list(null);
        return R.ok().data("list", courseList);
    }

前端接口部分。course.js.

 // 课程列表
  // Todo 
  getCourseList() {
    return request({
      url:"/eduservice/edu-course/getCourseList",
      method: 'get'
  })
  }

咱们拿着讲师列表过来快速改下。

<template>
  <div>
        <!--多条件查询表单-->
    <el-form
      :inline="true"
      class="demo-form-inline"
      style="margin-left: 20px; margin-top: 12px;"
    >
      <el-form-item label="课程标题">
        <el-input
          v-model="courseQuery.title"
          placeholder="请输入标题"
        ></el-input>
      </el-form-item>
      <el-form-item label="课程状态">
        <el-select v-model="courseQuery.status" placeholder="课程状态">
          <el-option label="已发布" :value='Normal'></el-option>
          <el-option label="未发布" :value='Draft'></el-option>
        </el-select>
      </el-form-item>
     
      <el-form-item>
        <el-button type="primary" icon="el-icon-search" @click="getList()"
          >查询</el-button
        >
        <el-button type="default" @click="resetData()">清空</el-button>
      </el-form-item>
    </el-form>

    <el-table
      :data="list"
      style="width: 100%"
      border
      fit
      highlight-current-row
      element-loading-text="数据加载中"
      v-loading="listLoading"
    >
      <el-table-column prop="number" label="序号" width="120" align="center">
        <template slot-scope="scope">
          {{ (page - 1) * limit + scope.$index + 1 }}
        </template>
      </el-table-column>
      <el-table-column prop="title" label="课程名称" width="280"> </el-table-column>
      <el-table-column label="状态" width="80">
        <template slot-scope="scope">
          {{ scope.row.status === 'Normal' ? "已发布" : "未发布" }}
        </template>
      </el-table-column>
      <el-table-column prop="price" label="价格" width="120" />
      <el-table-column prop="gmtCreate" label="添加时间" width="160" />
      <el-table-column prop="lessonNum" label="课时数" width="120" />
      <el-table-column label="操作" width="200" align="center">
        <template slot-scope="scope">
          <router-link :to="'/course/edit/' + scope.row.id">
            <el-button type="primary" size="mini" icon="el-icon-edit"
              >章节编辑</el-button>
               <el-button type="primary" size="mini" icon="el-icon-edit"
              >小节编辑</el-button>
          </router-link>
          <el-button
            type="danger"
            size="mini"
            icon="el-icon-delete"
            @click="removeDataById(scope.row.id)">删除</el-button
          >
        </template>
      </el-table-column>
    </el-table>

    <!--分页组件-->
    <el-pagination
               background
               layout="prev, pager, next,total,jumper"
               :total="total"
               :page-size="limit"
               style="padding: 30px 0; text-align: center"
               :current-page="page"
               @current-change="getList"
               >
    </el-pagination>

  </div>
</template>

<script>
// 调用course.js
import course from '@/api/edu/course'
export default {
  data() { // 定义变量及初始化数据
    return {
      page:1, //当前页
      limit:10,
      list:null, // 查询返回的记录集
      courseQuery:{}, // 查询的条件
      total:0 //总记录数
    }
  },
  created() {
    this.getList()
  },
  methods: {
    // 条件查询带分页 
    getList() {
      course.getCourseList()
      .then(
        response => {
          this.list =  response.data.list
      })
      .catch(
        error => {
          console.log(error)
      })
    },

  // 清空搜索条件
  resetData() {
    // 清空搜索框
   this.teacherQuery = {}
   // 显示全部表单数据
   this.getList()
  }

 
  }
}
</script>

看起来凑合着能看。这里暂时实现到这个程度,后续有时间进一步来完善。

image-20220120194610588

4.课程删除功能

需求如下。

image-20220120195518816

EduCourseController。

 @DeleteMapping("deleteCourse/{courseId}")
    public R deleteCourse(@PathVariable String courseId) {
        Boolean delete = eduCourseService.removeCourse(courseId);
        if(delete) {
            return R.ok();
        } else {
            return R.error();
        }

    }

EduCourseService.

 	 @Autowired
    EduCourseDescriptionService eduCourseDescriptionService;
    
    @Autowired
    EduChapterService eduChapterService;
    
    @Autowired
    EduVideoService eduVideoService;

 	@Override
    public Boolean removeCourse(String courseId) {
        // 1.删除课程小节
        eduVideoService.deleteVideo(courseId);
        // 2.删除课程章节
        eduChapterService.deleteChapter(courseId);
        // 3.删除课程描述
        eduCourseDescriptionService.deleteDescription(courseId);
        // 4.删除课程
        return baseMapper.deleteById(courseId) > 0;
    }

EduVideoServiceImpl

@Override
public void deleteVideo(String courseId) {
    QueryWrapper<EduVideo> wrapper = new QueryWrapper<>();
    wrapper.eq("course_id", courseId);
    baseMapper.delete(wrapper);
}

EduCourseDescriptionServiceImpl

    @Override
    public void deleteDescription(String courseId) {
        baseMapper.deleteById(courseId);
    }

deleteChapter之前实现过了,此处不赘述。前端部分请读者自行实现。后端部分使用swagger测试,如下。检验数据库,功能实现。

image-20220121205413858

5.阿里云视频点播

阿里云官网开通视频点播(推荐按照流量计费,不用不花钱)。

image-20220121212920492

阿里云视频点播服务提供API与SDK。API就是提供一些视频操作(上传、下载…)的固定url,我们通过提供的url拼接参数进行操作。SDK对API方式进行了封装,我们直接调用SDK中的类与方法来实现功能,使用更加方便,官方推荐使用。

在阿里云官网有具体的入门教程https://help.aliyun.com/document_detail/57756.htm。下面写一些简单demo入门。

在demo前,先提示一个细节:因为视频有加密的方式,加密视频不可以通过地址直接访问,因此我们在数据库中不存储视频的地址,而存储视频的Id。通过Id可以拿到视频的播放地址、凭证等信息。

5.1 获取视频地址

(1)引入依赖

service下新建Maven子模块service_vod。在其中pom.xml中引入依赖。

 <!--   视频点播对maven仓库的配置和依赖     -->
<repositories>
    <repository>
        <id>sonatype-nexus-staging</id>
        <name>Sonatype Nexus Staging</name>
        <url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
        <releases>
            <enabled>true</enabled>
        </releases>
        <snapshots>
            <enabled>true</enabled>
        </snapshots>
    </repository>
</repositories>    

 <!--   jar包依赖     -->
<dependencies>
        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>aliyun-java-sdk-core</artifactId>
        </dependency>
        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>aliyun-java-sdk-vod</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
        </dependency>
</dependencies>

(2)初始化操作

由于我们现在是demo,在test目录下新建com.wangzhou.vodtest包。InitObject类中创建DefaultAcsClient对象

package com.wangzhou.vodtest;

import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.profile.DefaultProfile;

//初始化类
public class InitObject {

    public static DefaultAcsClient initVodClient(String accessKeyId, String accessKeySecret) throws ClientException {
        String regionId = "cn-shanghai";  // 点播服务接入区域
        DefaultProfile profile = DefaultProfile.getProfile(regionId, accessKeyId, accessKeySecret);
        DefaultAcsClient client = new DefaultAcsClient(profile);
        return client;
    }

}

测试类。

public class TestVod {

    public static void main(String[] args) throws ClientException {

        // 1.创建客户端
        DefaultAcsClient client = InitObject.initVodClient("xxxx", "xxxx");
        // 2.创建request、response
        GetPlayInfoRequest request = new GetPlayInfoRequest();
        GetPlayInfoResponse response = new GetPlayInfoResponse();

        // 3.设置视频Id值
        request.setVideoId("xxxx");

        // 4.获取response
        response = client.getAcsResponse(request);

        List<GetPlayInfoResponse.PlayInfo> playInfoList = response.getPlayInfoList();
        //播放地址
        for (GetPlayInfoResponse.PlayInfo playInfo : playInfoList) {
            System.out.print("PlayInfo.PlayURL = " + playInfo.getPlayURL() + "\n");
        }
        //Base信息
        System.out.print("VideoBase.Title = " + response.getVideoBase().getTitle() + "\n");//VideoBase.Title = 6 - What If I Want to Move Faster.mp4
    }

}

读者可以自测。

5.2 获取视频凭证

对于加密视频,地址无法直接播放视频。我们需要得到视频的凭证。

private static void getVideoAuth() throws ClientException {
        // 1.创建客户端
        DefaultAcsClient client = InitObject.initVodClient("LTAI5tFgGXoyhNYWPMHf3gNz", "C8acTTWjFrySJIarGvSfJVTQNce1vt");
        // 2.创建request、response
        GetVideoPlayAuthRequest request = new GetVideoPlayAuthRequest();
        GetVideoPlayAuthResponse response = new GetVideoPlayAuthResponse();

        // 3.设置视频Id值
        request.setVideoId("77b9dd1391c54adf88d7b30efb5486f6");

        // 4.获取response
        response = client.getAcsResponse(request);

        //Base信息
        System.out.print("Video Auth = " + response.getPlayAuth() + "\n");//VideoBase.Title = 6 - What If I Want to Move Faster.mp4
    }
5.3 上传视频

(1)引入依赖

下面为官方教程中列出的依赖,在pom文件中添加,之前添加过的可以不要再添加。

<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId>aliyun-java-sdk-core</artifactId>
    <version>4.5.1</version>
</dependency>
<dependency>
    <groupId>com.aliyun.oss</groupId>
    <artifactId>aliyun-sdk-oss</artifactId>
    <version>3.10.2</version>
</dependency>
<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId>aliyun-java-sdk-vod</artifactId>
    <version>2.15.11</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.28</version>
</dependency>
<dependency>
    <groupId>org.json</groupId>
    <artifactId>json</artifactId>
    <version>20170516</version>
</dependency>
<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.8.2</version>
</dependency>

aliyun-java-vod-upload-1.4.14.jar未开源(其它jar包不需要),参考官网文档下载配置。

image-20220214203846415

参考官方代码进行测试。

  /**
     * 本地文件上传接口
     *
     * @param accessKeyId
     * @param accessKeySecret
     * @param title 上传后视频名
     * @param fileName 上传的文件路径
     */
    private static void testUploadVideo(String accessKeyId, String accessKeySecret, String title, String fileName) {
        UploadVideoRequest request = new UploadVideoRequest(accessKeyId, accessKeySecret, title, fileName);
        /* 可指定分片上传时每个分片的大小,默认为2M字节 */
        request.setPartSize(2 * 1024 * 1024L);
        /* 可指定分片上传时的并发线程数,默认为1,(注:该配置会占用服务器CPU资源,需根据服务器情况指定)*/
        request.setTaskNum(1);
        UploadVideoImpl uploader = new UploadVideoImpl();
        UploadVideoResponse response = uploader.uploadVideo(request);
        System.out.print("RequestId=" + response.getRequestId() + "\n");  //请求视频点播服务的请求ID
        if (response.isSuccess()) {
            System.out.print("VideoId=" + response.getVideoId() + "\n");
        } else {
            /* 如果设置回调URL无效,不影响视频上传,可以返回VideoId同时会返回错误码。其他情况上传失败时,VideoId为空,此时需要根据返回错误码分析具体错误原因 */
            System.out.print("VideoId=" + response.getVideoId() + "\n");
            System.out.print("ErrorCode=" + response.getCode() + "\n");
            System.out.print("ErrorMessage=" + response.getMessage() + "\n");
        }
    }
6.添加小节的视频上传功能
6.1 后端实现

(1)引入依赖

已完成.

(2)配置appliaction.properties

# 服务端口号
server.port=8003

# 服务名
spring.application.name=service_vod

# 环境设置 dev test prod
spring.profiles.active=dev

# 最大上传单个文件大小:默认1M
spring.servlet.multipart.max-file-size=1024MB
# 最大置总上传的数据大小 :默认10M
spring.servlet.multipart.max-request-size=1024MB

# 阿里云 vod
aliyun.vod.file.keyid=xxx
aliyun.vod.file.keysecret=xxx

上面vod部分请读者自己配置。

(3)创建启动类

package com.wangzhou.vod;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;

// 该模块与数据库无交互,加入exclude参数避免报错
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
// 配置包扫描规则,这样我们就能用到swager-ui等在com.wangzhou中存放的功能
@ComponentScan(basePackages = "com.wangzhou")
public class VodApplication {
    public static void main(String[] args) {
        SpringApplication.run(VodApplication.class ,args);
    }
}

(4)Controller

@EnableSwagger2
@RestController
@CrossOrigin
@RequestMapping("eduvod/video")
public class VodController {

    @Autowired
    private VodService vodService;

    @PostMapping("/uploadVideo")
    public R uploadVideo(MultipartFile file) {
        String videoId = vodService.uploadFile(file);
        return R.ok().data("videoId", videoId);
    }
}

(5)service

public interface VodService {

    String uploadFile(MultipartFile file);

}
@Service
public class VodServiceImpl implements VodService {

    /**
     * 文件流上传接口
     *
     */
    @Override
    public String uploadFile(MultipartFile file) {
        try {
            String videoId;
            String accessKeyId = ConstantVodUtils.ACCESSKEY_ID;
            String accessKeySecret = ConstantVodUtils.ACCESSKEY_SECRET;
            String fileName = file.getOriginalFilename();
            String title = fileName.substring(0, fileName.lastIndexOf("."));

            UploadStreamRequest request = new UploadStreamRequest(accessKeyId, accessKeySecret, title, fileName,file.getInputStream());
            UploadVideoImpl uploader = new UploadVideoImpl();

            UploadStreamResponse response = uploader.uploadStream(request);
            /* 如果设置回调URL无效,不影响视频上传,可以返回VideoId同时会返回错误码。其他情况上传失败时,VideoId为空,此时需要根据返回错误码分析具体错误原因 */
            videoId = response.getVideoId();
            return videoId;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

    }
}

(6)application.properties

# 服务端口号
server.port=8003

# 服务名
spring.application.name=service_vod

# 环境设置 dev test prod
spring.profiles.active=dev

# 最大上传单个文件大小:默认1M
spring.servlet.multipart.max-file-size=1024MB
# 最大置总上传的数据大小 :默认10M
spring.servlet.multipart.max-request-size=1024MB

# 阿里云 vod
aliyun.vod.file.keyid=LTAI5tFgGXoyhNYWPMHf3gNz
aliyun.vod.file.keysecret=C8acTTWjFrySJIarGvSfJVTQNce1vt

(7)ConstantVodUtils

@Component
public class ConstantVodUtils implements InitializingBean {
    @Value("${aliyun.vod.file.keyid}")
    private String keyId ;
    @Value("${aliyun.vod.file.keysecret}")
    private String keysecret;

    public static String ACCESSKEY_ID;
    public static String ACCESSKEY_SECRET;

    @Override
    public void afterPropertiesSet() throws Exception {
        ACCESSKEY_ID = keyId;
        ACCESSKEY_SECRET = keysecret;
    }
}

启动后端测试,出现“Could not resolve placeholder ‘xxx‘ in value “ x x x “ , 可 以 参 考 博 客 解 决 : [ C o u l d n o t r e s o l v e p l a c e h o l d e r ‘ x x x ‘ i n v a l u e “ {xxx}“,可以参考博客解决:[Could not resolve placeholder ‘xxx‘ in value “ xxx[Couldnotresolveplaceholderxxxinvalue{xxx}“_pangdongh的博客-CSDN博客_xxx](https://blog.csdn.net/pangdongh/article/details/105289199),在swager-ui:Swagger UI进行测试。

6.2 前端实现

(1)ui实现

之前在chapter.vue中预留了位置进行上传视频(todo),现在将这部分ui替换。

 <el-form-item label="上传视频">
    <el-upload
          :on-success="handleVodUploadSuccess"
          :on-remove="handleVodRemove"
          :before-remove="beforeVodRemove"
          :on-exceed="handleUploadExceed"
          :file-list="fileList"
          :action="BASE_API + 'eduvod/video/uploadVideo'"
          :limit="1"
          class="upload-demo"
          >
     <el-button size="small" type="primary">上传视频</el-button>
          <el-tooltip placement="right-end">
              <div slot="content">
                  最大支持1G,<br />
                  支持3GP、ASF、AVI、DAT、DV、FLV、F4V、<br />
                  GIF、M2T、M4V、MJ2、MJPEG、MKV、MOV、MP4、<br />
                  MPE、MPG、MPEG、MTS、OGG、QT、RM、RMVB、<br />
                  SWF、TS、VOB、WMV、WEBM 等视频格式上传
                      </div>
              <i class="el-icon-question" />
          </el-tooltip>
    </el-upload>
</el-form-item>

(2)数据定义

fileList: [], //上传文件列表
BASE_API: process.env.BASE_API, // 接口API地址

(3)方法实现

  // ------------------ 视频 -------------
   //上传成功执行方法
    handleVodUploadSuccess(response,file,fileList){
        this.video.videoSourceId = response.data.videoId
        this.video.videoOrignalName = file.name
    },
    beforeVodRemove(file, fileList){
      return this.$confirm(`确定移除 ${ file.name }`);
    },
6.3 nigix配置

(1)配置上传文件大小限制

client_max_body_size 1024m;

image-20220216214328867

(2)配置转发端口

 location ~ /eduvod/ {
         proxy_pass http://localhost:8003;
 }

(3)重启nigix。

# 关闭nginx
nginx.exe -s stop

# 启动nginx
nginx.exe
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

谷粒学院day09——课程发布与阿里云视频点播服务 的相关文章

  • Ubuntu软件源、pip源大全,国内网站网址,阿里云、网易163、搜狐、华为、清华、北大、中科大、上交、山大、吉大、哈工大、兰大、北理、浙大

    文章目录 一 企业镜像源 1 阿里云 2 网易163 3 搜狐镜像 4 华为 二 高校镜像源 1 清华源 2 北京大学 3 中国科学技术大学源 USTC 4 上海交通大学 5 山东大学 6 吉林大学开源镜像站 7 哈尔滨工业大学开源镜像站
  • 如何优雅做好项目管理?

    导言 项目本身无好坏之分 项目管理有做好与做坏之别 在互联网大厂的体制下 想要做坏一个项目很难 可以通过换人 追加资源等方式消除风险 想要做好一个项目不容易 需要团队及PM付出大量心血和精力 在这些做好的项目中 我们也观察到很多PM做的疲惫
  • 什么是住宅ip,静态和动态怎么选?

    上文我们介绍了数据中心代理 这次我们来介绍下住宅代理ip 住宅代理ip分类两种类型 静态住宅代理和动态住宅代理 他们有什么区别又能用在什么场景呢 我们先从他们是如何运作开始 一 什么是住宅代理ip isp住宅代理ip我们称为真人住宅代理 地
  • 极氪汽车的云资源治理细探

    作者 极氪汽车吴超 前言 2021 年 极氪 001 迅速崭露头角 仅用 110 天便创下了首款车型交付量 最快破万 的纪录 2022 年 11 月 极氪 009 在短短 76 天内便率先完成了首批交付 刷新了中国豪华纯电品牌交付速度的纪录
  • 国际版腾讯云/阿里云:全站加快有哪些功用?有哪些优势?适用于什么场景?

    腾讯云全站加快有哪些功用 有哪些优势 适用于什么场景 产品功用 全站加快 ECDN 经过在全球各区域部署加快节点 有用下降跨国拜访推迟 保证全球加快作用 最优链路 各加快节点两两相连 实时勘探 结合腾讯自研的最优链路算法 获取用于传输的最优
  • typora+阿里云OSS+PicGO进行图床设置

    typora 阿里云OSS PicGO进行图床设置 文章目录 typora 阿里云OSS PicGO进行图床设置 前言 crystal ball 一 阿里云OSS设置 satellite 1 进入 阿里云OSS官网 https www al
  • 课时 10 自测题

    使用存储快照功能需要用到哪些 Kubernetes API 资源对象 多选题 A VolumeSnapshot B VolumeSnapshotClass C VolumeSnapshotContent D PersistentVolume
  • 阿里云服务器包年包月、按量和抢占式实例怎么选?区别是什么?

    阿里云服务器ECS付费类型包年包月 按量付费和抢占式实例有什么区别 包年包月先付费后使用 最低购买一个月时长 平均下来成本较低 按量付费先使用后付费 按小时结算费用 适合短期使用 平均下来费用要比包年包月贵一些 抢占式实例和按量付费相类似
  • 阿里服务器怎么用教程[第一部分]

    第一步 登录我们的阿里云账号 第二步 根据自己的具体情况 选择好服务器的配置 比如你是大型企业 预估网站访问量很大 那么就要选配置较好的服务器 如果是个人网站 预估流量较小 就可以选择配置较低的云服务器 第三步 购买好云服务器后 我们在阿里
  • ECS共享型s6和ECS突发性能型t6的区别选择哪个好?

    WP建站 一个专注于wordpress学习的 关注他 2 人赞同了该文章 这两个类型的阿里云ecs服务器的话 一般在这两个中二选一的话我们建议优先选择ECS共享型s6 我们简单的来说说他们的一些区别和特点吧 首先我们要知道的是他们都是独立的
  • el-upload上传阿里云(oss上传)

    oss上传 在你的项目安装oss npm install ali oss save 初始化oss 配置文件 新建一个js文件 内容如下 代表你所申请的参数 问运维要 let OSS require ali oss export let cl
  • kata-containers的阿里云镜像仓库地址

    kata containers的阿里云镜像仓库地址 关键字 kata containers 阿里云 镜像 kata containers需要安装在Centos8 如下为 etc yum repos d kata containers的内容
  • 【教程】详解如何将云服务器从一个平台转移到腾讯云服务器

    转载请注明出处 小锋学长生活大爆炸 xfxuezhang cn 背景介绍 我现在有一台华为云服务器 但是快到期了 考虑到腾讯云服务器比较便宜 可以看这里 特惠产品合集页 因此想转过来 但华为云上东西 环境都存满了 如果重新搭建 那未免太麻烦
  • 三分钟阿里云服务器u1通用算力型性能、使用限制及费用说明

    阿里云服务器u1是通用算力型云服务器 CPU采用2 5 GHz主频的Intel R Xeon R Platinum处理器 通用算力型u1云服务器不适用于游戏和高频交易等需要极致性能的应用场景及对业务性能一致性有强诉求的应用场景 比如业务HA
  • 阿里云CDN架构接入WAF应用防火墙案例实践

    文章目录 1 网站架构变化 2 配置WAF应用防火墙 2 1 配置网站接入WAF防火墙 2 2 WAF防火墙生成CNAME地址 2 3 配置WAF防火墙HTTPS证书 2 4 WAF防火墙开启HTTP回源SLB 3 配置CDN加速器回源WA
  • 应用编排与管理:核心原理

    本节课程要点 K8s 资源的重要元信息 使用阿里云服务演示一下如何去修改或查看 K8s 重要元数据 详细分析控制器模式 总结控制器模式特点 资源元信息 1 Kubernetes 资源对象 首先 我们来回顾一下 Kubernetes 的资源对
  • 逍遥子突然辞去阿里一切职务!之前不再担任董事长,现在阿里云CEO也卸了

    金磊 发自 凹非寺量子位 公众号 QbitAI 阿里巴巴史上最大架构重组仅半年后 再次迎来重大变革 原集团CEO张勇 花名 逍遥子 正式卸任 同时辞去阿里云董事长和CEO职务 这一次 阿里巴巴掌门的接力棒交到了蔡崇信和吴泳铭 花名 东邪 手
  • 线上阿里云短信盗刷问题实录

    背景 营销系统中有定时任务处理将待支付订单变更为已取消 执行时间五分钟一次 业务执行处理异常会发送短信给相关开发人员进行短信提醒 从下午一点二十五开始 开发人员间隔五分钟就会收到业务执行异常的短信提醒 最初因为测试或是正式环境中确实有异常的
  • 阿里云添加二级域名

    阿里云添加二级域名 1 申请配置域名 1 1 购买域名 1 2 登录到阿里云服务器控制台 进入云解析DNS页面 1 3 点击 域名解析 1 4 点击 添加记录 1 5 进行txt校验 1 6 添加记录
  • 阿里云2核4G服务器优惠价格30元3个月?小心坑

    2024年阿里云2核4G服务器优惠价格30元3个月 活动 https t aliyun com U bLynLC 配置为云服务器ECS经济型e实例ecs e c1m2 large 3M固定带宽 系统盘为40GB ESSD Entry 活动打

随机推荐

  • CTFHUB-WEB

    HTTP协议 题目 请求方式 思路一 我们知道http请求方式中没有CTFB方式 就想到CTFHUB 使用BP抓包 将原来的数据包请求方式GET改成CTFHUB 点击Forward 放包 得到flag 积累 HTTP协议的八种请求方式 1
  • 典型案例 3:十分钟搭建弹性可扩展的 Web API

    作者 萧起 阿里云云原生团队 导读 本节课程主要分为三个部分 基本概念中介绍基于函数计算的 WebAPI 与普通的 WebAPI 的区别及优势 开发流程中介绍如何在函数计算的控制台进行 WebAPI 的开发 操作演示中会实例演示函数计算 W
  • mysql-索引_MySQL-索引

    mysql 索引 MySQL 索引 MySQL INDEXES A database index is a data structure that improves the speed of operations in a table In
  • 基于MATLAB的图像复原视图分析技术

    基于MATLAB的图像复原视图分析技术 摘要 图像质量的好与坏受很多方面因素的影响 其中运动模糊以及失真是较为主要的因素 这些因素贯穿在图像获取 传输以及储存的全过程中 本次设计用到的是MATLAB软件然后进行仿真 对模糊图像建立退化模型
  • Spring系列文章:Spring事务

    一 事务简述 1 什么是事务 Transaction tx 在 个业务流程当中 通常需要多条DML insert delete update 语句共同联合才能完成 这 多条DML语句必须同时成功 或者同时失败 这样才能保证数据的安全 多条D
  • 单机redis和redisCluster集群获取所有key

    对于单机redis keys 对于redis cluster集群 redis cli c a CLUSTER AUTH cluster call CLUSTER IP CLUSTER POPRT keys 如 redis cli c clu
  • L1正则和L2正则的比较分析详解

    感受 上次有个面试官问我l1正则和l2正则有什么区别 当时把我给问傻了 于是就回来找了资料写了这篇博客 我参照的是英文博客 吸取别人的长处 希望能帮助大家 如有错误或者需要补充的 欢迎指正 咱们共同进步 范数 norm 数学上 范数是一个向
  • actuator--基础--6.2--端点解析--metrics端点

    actuator 基础 6 2 端点解析 metrics端点 代码位置 https gitee com DanShenGuiZu learnDemo tree master actuator learn actuator01 1 介绍 用于
  • Ubuntu更改默认python版本的两种方法 python-> Anaconda

    当你安装 Debian Linux 时 安装过程有可能同时为你提供多个可用的 Python 版本 因此系统中会存在多个 Python 的可执行二进制文件 你可以按照以下方法使用 ls 命令来查看你的系统中都有那些 Python 的二进制文件
  • 谷歌首页被360篡改

    打开浏览器后 右上角设置找到跳转到360的地址将其删除
  • 海明码校验【简单详细】

    海明码 1 什么是海明码 一个名叫Richard Hanming老爷爷在1950年提出的检验纠错方法 它具有一位纠错能力 2 海明码的计算方法 设欲检测的二进制代码为n位 K为检测位 提供纠错 总共n k位代码 当中检测位满足的关系 2 k
  • 将自己数据集转化为lmdb格式

    在caffe master github examples imagenet 路径下有convert imagenet sh文件 使用时有以下注意事项 注意点写在了代码注释里 usr bin env sh Create the imagen
  • Spring boot 日志框架

    SpringBoot能自动适配所有的日志 而且底层使用slf4j logback的方式记录日志 引入其他框架的时候 只需要 把这个框架依赖的日志框架排除掉即可
  • msvcr120.dll丢失怎样修复win11

    msvcr120 dll丢失怎样修复 相信这个问题困扰着不少小伙伴 msvcr120 dll是Windows系统中非常重要的组件 丢失或者损坏会导致很软件跟游戏无法打开运行 小编今天就把修复教程分享给大家 修复方法如下 首先打开电脑浏览器以
  • 1.pom.xml文件 - pom.xml说明

    史上最全的 pom xml 文件详解 史上最全的 pom xml 文件详解 雨雾清影的博客 CSDN博客 pom xml 参考 Maven的pom xml文件详解 Build Settings tomato 的博客 CSDN博客 pom中b
  • java 兔子生兔子

    标题 兔子生兔子 问题描述 假设一对兔子的成熟期是一个月 即一个月可长成成兔 那么 如果每对成兔每个月都生一对小兔 一对新生的小兔从第二个月起就开始生兔子 试问从一对兔子开始繁殖 以后每个月会有多少对兔子 题目要求 要求输入 输出格式中应包
  • 信息学奥赛一本通 1618:越狱

    题目链接 http ybt ssoier cn 8088 problem show php pid 1618 思路 总方案数为 m m m
  • java.lang.ClassNotFoundException: solr.DataImportHandler

    错误信息截图 2018 08 02 07 56 17 527 INFO qtp817348612 15 x mysql2solr o a s m r SolrJmxReporter Closing reporter org apache s
  • 【系统设计系列】缓存

    系统设计系列初衷 System Design Primer 英文文档 GitHub donnemartin system design primer Learn how to design large scale systems Prep
  • 谷粒学院day09——课程发布与阿里云视频点播服务

    day9 课程信息确认与视频点播 1 课程信息确认 1 1 后端实现 1 2 前端实现 2 课程的最终发布 3 课程列表功能 4 课程删除功能 5 阿里云视频点播 5 1 获取视频地址 5 2 获取视频凭证 5 3 上传视频 6 添加小节的