nodejs服务代码
const express = require("express");
const fs = require("fs");
const app = express();
// 展示html页面
app.get("/", function (req, res) {
res.sendFile(__dirname + "/index.html");
});
// nodejs切片读取文件示例
if (!Date.now()) {
let i = 0
const readableStream = fs.createReadStream('asai.mp4') + fs.createReadStream('asai1.mp4');
readableStream.on('data', (chunk) => {
i++
console.log(666.123465, i, chunk);
});
readableStream.on('end', () => {
console.log('数据读取完毕');
});
}
// nodejs合并切片示例
// 方案一
if (!Date.now()) {
const writeStream = fs.createWriteStream('asai-a.mp4');
let readStream
function concatFiles(readfiles) {
if (readfiles && readfiles[0]) {
console.log(666.30002, readfiles[0])
readStream = fs.createReadStream(readfiles[0]);
readStream.pipe(writeStream, { end: false })
readStream.on('end', () => {
readfiles.shift()
concatFiles(readfiles);
});
} else {
writeStream.close();
}
readStream.on('error', (error) => { // 监听错误事件,关闭可写流,防止内存泄漏
console.error(666.789, error);
writeStream.close();
});
}
concatFiles(['asai.mp4', 'asai1.mp4'])
}
// 方案二
if (!Date.now()) {
// 读取一个文件,使用fs读取文件获取一个Buffer类型数据
const buffer = fs.readFileSync('asai.mp4')
// 将文件进行切分
const file1 = buffer.subarray(0, 3000000)
const file2 = buffer.subarray(3000000)
// 全并多个切片文件
const allfile = Buffer.concat([file1, file2])
console.log(666.2008, allfile)
// 读取文件切片
const s1 = fs.readFileSync('asai.mp4')
const s2 = fs.readFileSync('asai1.mp4')
// 将读取的文件切片合并
const bb = Buffer.concat([s1, s2])
// 将合并的切片数据,写到一个新文件中
fs.writeFileSync('asai-c.mp4', bb)
}
// 多视频切片发送示例(咱不支持)
const videoData = {
list: [
// { path: 'asai1.mp4', size: fs.statSync('asai1.mp4').size },
{ path: 'asai.mp4', size: fs.statSync('asai.mp4').size },
],
index: 0,
curStart: 0,
curSize: 0,
totalSize: 0
}
videoData.totalSize = videoData.list.reduce((prev, cur) => {
return prev + cur.size
}, 0)
app.get("/video", function (req, res) {
const range = req.headers.range;
if (range) {
resVideo(res, range)
} else {
const path = 'asai.mp4'
const fileSize = fs.statSync(path).size
let head = {
'Content-Length': fileSize,
'Content-Type': 'video/mp4',
};
res.writeHead(200, head);
fs.createReadStream(path).pipe(res);
res.status(400).send("Requires Range header");
}
});
app.listen(8000, function () {
console.log("Listening on port 8000!");
});
function resVideo(res, range) {
console.log(666.10001, range)
let [, start, end] = range.match(/(\d*)-(\d*)/);
if (videoData.list[videoData.index]) {
// if (start < videoData.curSize) {
// start = videoData.curSize
// }
// range = 'bytes=1458753-';
// 保证request的header里面有range,没有range就无法判断需要把哪一部分content写入response
// const videoPath = "asai.mp4";
const videoPath = videoData.list[videoData.index].path;
const videoSize = videoData.list[videoData.index].size;
// 还需要资源的路径和资源的大小,资源的大小会用来计算哪一部分content要被send
const CHUNK_SIZE = 10 ** 5; // 10 ** 6 ≈ 1MB
// const start = Number(range.replace(/\D/g, "")); // 截取开始位置
// 这里规定每次返回1M的内容,开始位置从request的header里获取并将其转成Number类型
end = Math.min(end ? end : start + CHUNK_SIZE, videoData.curStart + videoSize - 1); // 截取结束位置
console.log(666.10002, videoPath, { start, end, size: videoData.totalSize, startv: start - videoData.curStart, endv: end - videoData.curStart, sizev: videoSize }, (start >= videoData.curStart && start < end), videoData)
// 这里需要使用fs来创建一个videoSteam,使用videoPath和start和end作为参数
const videoStream = fs.createReadStream(videoPath, { start: start - videoData.curStart, end: end - videoData.curStart });
// const newRange = `bytes ${start}-${end}/*`
// const newRange = `bytes ${start}-${end}/1509200`
// const newRange = `bytes ${start}-${end}/${videoSize}`
const newRange = `bytes ${start}-${end}/${videoData.totalSize}`
// 视频发送状态
console.log(666.789, newRange)
// 在响应头里面我们需要返回Content的大小,Content-range,Accept-ranges,Content-type。
const headers = {
"If-Range": "Etag",
"Content-Range": newRange,
"Accept-Ranges": "bytes",
"Content-Type": "multipart/byteranges",
// "Content-Type": "video/mp4",
// "Content-Length": CHUNK_SIZE,
"Transfer-Encoding": "chunked",// 假如不知道内容长度,代替Content-Length
};
// 状态码设置为206表明我们返回的是部分内容。
res.writeHead(206, headers);
// 把videoStream pipe到response即可
videoStream.pipe(res);
// 手工记录cursize
videoData.curSize = end
if (end - videoData.curStart === videoSize - 1 && videoData.list[videoData.index + 1]) {
videoData.curStart += videoData.list[videoData.index].size
videoData.index += 1
console.log(666.123, videoPath, 'send over.', videoData)
}
}
}
前端html页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>视频/文件切片请求处理nodejs+html</title>
<style>
body {
max-width: 100%;
height: 100vh;
background-color: rgb(14, 14, 14);
display: flex;
margin: auto;
align-items: center;
justify-content: center;
}
</style>
</head>
<body>
<video height="100%" src="/video" controls autoplay muted></video>
</body>
</html>
附件:前端js切片上次
<html lang="zh-cn">
<head>
<meta title="文件切片合并" />
</head>
<body>
<!-- 页面选择文件 -->
<input type="file" id="file" />
<!-- 将加载的视频文件进行切片后合并,并播放 -->
<video id="play" controls style="width:500px;height:auto"></video>
</body>
<script>
file.addEventListener('change', async (e) => {
// 获取FileList中的File文件
let file2 = file.files[0]
// ----------第一种方式-------------------
// 使用Blob的slice方法切片,使用Blob构造函数进行数据合并
// 将加载的文件进行切片
// let s1 = file2.slice(0,100)
// let s2 = file2.slice(100)
// 将切片的文件进行数据合并
// let newFile = new Blob([s1,s2])
// -----------第二种方式------------------
// 如何对大文件进行流式读取
let chunckArr = []
// 通过Blob获取 ReadableStream 流对象,再获取可读的流读取器
let reader = file2.stream().getReader()
let done = false
while (!done) {
// 使用流读取器的read方法,获取流队列中的下一个分块数据
let { value, done: readDone } = await reader.read()
console.log(value)
chunckArr.push(value)
done = readDone
}
// 通过Blob构造函数合并流数据
let newFile = new Blob(chunckArr)
// 进行页面播放
play.setAttribute('src', URL.createObjectURL(newFile))
})
</script>
</html>
文章来源地址https://www.toymoban.com/news/detail-654709.html
文章来源:https://www.toymoban.com/news/detail-654709.html
到了这里,关于nodejs与前端js大文件、切片、视频流相关技术示例的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!