最近有视频上传的需求,所以决定写几篇文章来讲解一下。这些文章能写到的内容如下:
那接下来,咱们就开始进入正题吧:
前端搭建就不说了,使用create-react-app脚手架
可以快速的帮你搭建好一个react项目,当然你也可以使用我写出来的工具lazy-create-react-app去搭建一个非常简单的react项目。如果你真的想用我这个工具,请运行:npx lazy-create-react-app [自定义项目名称]。
后端我们使用express来搭建后端工程。具体操作如下:
// 1、第一步(安装express脚手架)
npm install -g express-generator
// 2、第二步(使用脚手架创建一个名为“end”的项目)
express end
// 3、第三步(进入项目目录)
cd end
// 4、第四步(安装项目依赖)
npm install
// 5、修改后端项目的端口号
// 6、第五步(启动项目)
npm run start
修改后端的端口号如下,否则前后端项目同时启动的时候,他们两个会因为使用相同的端口号而导致冲突:
接着,我们来为本次的内容注册专门的路由:
routes目录下新增video.js文件,video.js文件内容如下:
var express = require('express');
var router = express.Router();
router.get('/', (req, res, next) => {
res.send({ msg: '路由注册成功' });
});
module.exports = router;
紧接着,我们来修改下app.js文件内容:
// 其余文件内容不变 =====
var videoRouter = require('./routes/video');
app.use('/video', videoRouter);
// 其余文件内容不变 =====
至此,初始化的工作就完毕了。
后端需要安装cors依赖,请执行如下命令:
npm install cors --save
依赖安装成功后,继续修改app.js文件内容:
// 其余文件内容不变 =====
var cors = require('cors');
app.use(cors());
// 其余文件内容不变 =====
接下来,我们在前端项目
里新增一个Video页面,但是在操作前,我们还需要安装2个依赖,分别是react-router、axios。请执行以下命令:
npm install react-router axios --save
修改前端项目里的index.js文件,并新增Video文件。
// app.js文件如下:
import { createBrowserRouter, RouterProvider, Route } from 'react-router-dom';
import Video from './Video';
const router = createBrowserRouter([
{
path: '/',
element: <App />,
},
{
path: '/video',
element: <Video/>
}
]);
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<RouterProvider
router={router}
/>
</React.StrictMode>
);
Video文件内容如下:
import React from 'react';
import axios from 'axios';
let axiosInstance = axios.create({
baseURL: 'http://127.0.0.1:8888',
});
class Video extends React.Component {
constructor(props){
super(props);
}
testConnect = async () => {
let result = await axiosInstance.get('/video');
if (result?.data?.code == 200){
alert(result?.data.msg);
}
}
render(){
return <div>
<button onClick={this.testConnect}>测试连接</button>
</div>
}
}
export default Video;
访问Video页面,并且点击按钮,如果你的界面是下面这样,那么恭喜你,环境搭建算是成功了。
首先来思考一下,前端有哪些方式可以上传视频?似乎只有input[type='file']标签
才能上传视频吧,那顺着这个思路走,接下来该如何拿到上传的文件呢?
2种方式
,并且这2种方式是等价的。
那这两种方式应该在代码里的哪个位置去写呢?
答案是在input标签的change事件里写。但是需要注意一下,如果你上传的是同一个文件,那么第二次上传视频时是不会再触发change事件的。
接下来我们来具体看一下代码,修改Video.js文件如下:
// 其余文件内容不变=======
class Video extends React.Component {
// 其余文件内容不变=======
inputChange = async (event) => {
console.log('uploadFileObj:', event.target.files)
console.log('input-files:', document.querySelector('input').files)
}
render(){
return <div>
<input
type='file'
onChange={(event) => this.inputChange(event)}
/>
</div>
}
}
// 其余文件内容不变=======
此时我们上传一个视频,看看控制台里的打印:
我们会发现,这2种方式拿到的都是fileList对象。
什么是fileList?
它是一个类数组对象,类数组对象里的每一项都是File对象。File对象又继承Blob对象。
下图可能会更清晰:
上一小节里,我们知道了如何拿到上传的文件,这一节我们来讲一下如何处理视频数据。
2种方式如下:
这2种方式的区别如下:
现在来具体解释一下:
1、扩展性强。这代表着第二种方式可以做出亲民的效果。因为fileReader提供了很多的API、事件,所以它能让用户在程序里去预览上传后的文件。再比如本地文件上传中的loading效果。
2、处理数据更灵活。它可以将数据处理为base64,或者二进制数据。只需要调用相应的API即可。
3、第一种方式更暴力。这个主要是针对那些对本地预览没有要求的场景,直接以FormData的数据格式发送到后端。细粒度肯定不如第二种方式。
我们这里决定使用更暴力的那一方,就是File对象的方式。
let axiosInstance = axios.create({
baseURL: 'http://127.0.0.1:8888',
});
class Video extends React.Component {
uploadChunkReq = async (fileBlob) => {
let result = await axiosInstance.post(
'/video/uploadChunk',
{videoDict: fileBlob},
{
headers: {
'Content-Type': 'multipart/form-data'
}
}
);
return result;
}
// 上传文件后触发
inputChange = async (event) => {
let self = this;
let uploadFileObj = event.target.files[0] || {};
await self.uploadChunkReq(uploadFileObj);
}
render(){
return <div>
<input
type='file'
onChange={(event) => this.inputChange(event)}
/>
</div>
}
}
特别注意!!!
这里对content-type
类型值为FormData
做一个解释,这个点也是很多人遗漏的点。我们如何告诉后端,我们的请求体是一个表单呢?需要满足以下2点:
只要请求体是一个对象就可以,不一定非要是formData对象
。后端在接收到这样请求后,需要去处理Content-Type类型为“multipart/form-data”的数据,但是express并没有预制处理它的能力。所以我们只能是下载一个第三方库multer
。multer只是其中的一个代表,你也可以选择其他第三方库,比如:connect-multiparty
。
接下来就是multer库
的用法,想知道这些API都干了哪些事的话可以去看看官网。这里只能保证你可以实现这个功能。
此时我们来修补一下后端对应的接口:
// video.js文件内容如下:
var express = require('express');
var multer = require("multer");
var router = express.Router();
// 定义文件的存放路径
var tempChunkPosition = multer({
storage: multer.diskStorage({
destination: function (req, file, cb) {
cb(null, path.join(__dirname, '../tempChunk'))
},
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now() + '.mp4')
}
})
});
router.post('/uploadChunk', tempChunkPosition.single("videoDict"),(req, res, next) => {
let fileInfo = req.file;
return res.send({
success: true,
msg: '上传成功',
data: {
filePath: fileInfo.path
}
});
});
module.exports = router;
当我们在前端上传一个mp4的视频时,此时在本地我们就可看到在tempChunk目录下,成功的保存了上传的视频。
好啦,我们本篇文章就结束了。这里面我们只是实现了一个最最基本普通的视频上传保存功能,接下来的文章会基于这篇文章来做讲解,比如我们还要实现文件的分片上传、断点续传、分片下载等功能。如果上述过程中出现了失误,还请各位大佬指点迷经。
本人最近建立了一个交流群,这个交流群不仅局限于前端,你想要探讨任何一项技术都可以,因为是刚刚建立,所以群里会比较清净,想要加入群聊的,欢迎添加我的微信:18845097791
最后,希望我的文章对你有所帮助,我们下期再见啦~~