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

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
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

半旧518

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值