day9 课程信息确认与视频点播
1.课程信息确认
1.1 后端实现
到目前为止,我们已经完成了上图的第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自测。
出了点问题。
看起来是因为找不到mapper文件。
这是由于maven的默认加载机制,只会把src/main/java下的java文件加载,该目录下其它文件不会加载。而我们的xml文件放在下图位置,显然是不会被加载的。
看看文件的输出目录。果然没有加载这些xml文件。
解决方法有三种:
(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后面。
注:classpath就是指src下的路径,或者说是tagret下的classes路径(参考下图),注意改成自己的路径。
重启项目,测试通过。(注:如果文件没有被复制过去手动拷贝吧)
1.2 前端实现
现在后端部分已经完成了,接下来要实现前端部分,首先我们要从课程信息的页面跳转到信息确认的页面,这个功能之前在
chapter.vue的next()
方法中已经实现了。
在publish.vue页面需要做两件事情:从路由中得到courseId
,调用接口显示数据。
(1)先实现前端的接口course.vue。
// 根据id查询课程确认信息
getpublishCourseInfo(id){
return request({
url:`/eduservice/edu-course/getpublishCourseInfo/${
id}`,
method: 'get',
})
}
(2)从路由中拿到courseId
publish.vue
created() {
if(this.$route.params && this.$route.params.id) {
this.courseid = this.$route.params.id
}
}
(3)调接口
import publishCourse from "@/api/edu/course.js";
getpublishCourseInfo(){
publishCourse.getpublishCourseInfo(this.courseid)
.then(resp => {
this.publishCourse = resp.data.publishCourse
})
}
(4)显示数据
(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>
课程确认信息的显示就实现了。测试如下。
2.课程的最终发布
现在课程虽然已经添加到了数据库了,但是课程还没有真正的发布:只有在课程发布以后用户才可以看到课程。在数据库中有一个字段status
,发布的课程会将其置成Normal
。
因此,所谓的课程发布其实是修改操作,我们可以直接调用之前的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.课程列表功能
需求如下图。与之前的讲师列表内容基本是一致的。
先来实现最简单的部分:数据的展示。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>
看起来凑合着能看。这里暂时实现到这个程度,后续有时间进一步来完善。
4.课程删除功能
需求如下。
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测试,如下。检验数据库,功能实现。
5.阿里云视频点播
阿里云官网开通视频点播(推荐按照流量计费,不用不花钱)。
阿里云视频点播服务提供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包不需要),参考官网文档下载配置。
参考官方代码进行测试。
/**
* 本地文件上传接口
*
* @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“,可以参考博客解决:[Couldnotresolveplaceholder‘xxx‘invalue“{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;
(2)配置转发端口
location ~ /eduvod/ {
proxy_pass http://localhost:8003;
}
(3)重启nigix。
# 关闭nginx
nginx.exe -s stop
# 启动nginx
nginx.exe