nodejs与前端js大文件、切片、视频流相关技术示例

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 = 0const 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 readStreamfunction 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).sizelet 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要被sendconst 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);// 手工记录cursizevideoData.curSize = endif (end - videoData.curStart === videoSize - 1 && videoData.list[videoData.index + 1]) {videoData.curStart += videoData.list[videoData.index].sizevideoData.index += 1console.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 = falsewhile (!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>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/40397.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

redis-配置详解(基础篇)

# 可以配置包含其他redis配置&#xff0c;可以使得配置更清晰化 # include /path/to/local.conf ################################## MODULES ##################################### # Load modules at startup. If the server is not able to load modules # it will abor…

拉丁方设计资料的方差分析(SPSS版+SAS版)

拉丁方设计&#xff08;Latin square design&#xff09;&#xff1a;实验研究中涉及一个处理因素和两个控制因素&#xff0c;每个因素的类别数或水平数相等&#xff0c;此时可采用拉丁方设计&#xff0c;将两个控制因素分别安排在拉丁方设计的行和列上。该设计类型仍为单因素方…

中电金信通过KCSP认证 云原生能力获权威认可

中电金信通过KCSP&#xff08;Kubernetes Certified Service Provider&#xff09;认证&#xff0c;正式成为CNCF&#xff08;云原生计算基金会&#xff09;官方认证的 Kubernetes 服务提供商。 Kubernetes是容器管理编排引擎&#xff0c;底层实现为容器技术&#xff0c;是云原…

ABAP 新语法--Data Processing

1. String Template 新语法引入了字符串模板&#xff0c;用于处理字符串连接以及格式转换 字符串模板在 | … | 之间定义&#xff0c;主要分为两部分&#xff0c;固定文本和变量 其中&#xff0c;变量只能在 { … } 内使用&#xff0c;大括号之外的所有字符均作为固定文本使用…

【Swagger】只需要三步,就可以让你的项目实现Swagger在线文档,实时浏览,修改展示

目录 1. pom.xml文件中添加Swagger的jar包 2. 配置Swagger 3. 项目启动中加入Swagger注解的开关&#xff0c;启动Swagger功能 4. 启动项目&#xff0c;查看效果 Swagger 的功能这里就不多说明了&#xff0c;相信大家都懂的&#xff0c;好奇多问一句&#xff0c;大家有知道其…

zookeeper安装配置采坑流程

安装 wget https://dlcdn.apache.org/zookeeper/zookeeper-3.8.2/apache-zookeeper-3.8.2-bin.tar.gz解压&#xff1a; tar -zxvf apache-zookeeper-3.8.2-bin.tar.gz如下 bin目录下文件是可执行文件 conf目录文件是配置文件 修改zoo.cfg&#xff08;复制zoo_sample&#x…

从0搭建ECG深度学习网络

本篇博客介绍使用Python语言的深度学习网络&#xff0c;从零搭建一个ECG深度学习网络。 任务 本次入门的任务是&#xff0c;筛选出MIT-BIH数据集中注释为[‘N’, ‘A’, ‘V’, ‘L’, ‘R’]的数据作为本次数据集&#xff0c;然后按照8&#xff1a;2的比例划分为训练集&…

什么是DNS服务器的层次化和分布式?

DNS (Domain Name System) 的结构是层次化的&#xff0c;意味着它是由多个级别的服务器组成&#xff0c;每个级别负责不同的部分。以下是 DNS 结构的层次&#xff1a; 根域服务器&#xff08;Root Servers&#xff09;&#xff1a; 这是 DNS 层次结构的最高级别。全球有13组根域…

【云原生】Docker 详解(二):Docker 架构及工作原理

Docker 详解&#xff08;二&#xff09;&#xff1a;Docker 架构及工作原理 Docker 在运行时分为 Docker 引擎&#xff08;服务端守护进程&#xff09; 和 客户端工具&#xff0c;我们日常使用各种 docker 命令&#xff0c;其实就是在使用 客户端工具 与 Docker 引擎 进行交互。…

[oneAPI] 手写数字识别-LSTM

[oneAPI] 手写数字识别-LSTM 手写数字识别参数与包加载数据模型训练过程结果 oneAPI 比赛&#xff1a;https://marketing.csdn.net/p/f3e44fbfe46c465f4d9d6c23e38e0517 Intel DevCloud for oneAPI&#xff1a;https://devcloud.intel.com/oneapi/get_started/aiAnalyticsToolk…

Curson 编辑器

Curson 汉化与vacode一样 Curson 自带chat功能 1、快捷键ctrlk(代码中编辑) 2、快捷键ctrll 右侧打开窗口

为什么hive会出现_HIVE_DEFAULT_PARTITION分区

问题&#xff1a; 为什么hive表中出现_HIVE_DEFAULT_PARTITION分区&#xff1f; 解答&#xff1a; 因为在业务sql中使用的是动态分区&#xff0c;并且hive启用动态分区时&#xff0c;对于指定的分区键如果存在空值时&#xff0c;会对空值部分创建一个默认分区用于存储该部分…

小程序项目组件的基本应用

宿主环境&#xff1a;程序运行必须依赖的环境 小程序的宿主环境 ---->手机微信(定位、扫码、支付等) 小程序的通信模型&#xff1a; 渲染层和逻辑层之间的通信(微信客户端转发)逻辑层和第三方服务器之间的通信(微信客户端转发) 小程序的运行机制&#xff1a; 启动&#xff1…

c#实现工厂模式

可以使用以下代码实现C#中的工厂模式&#xff1a; 首先&#xff0c;定义一个接口作为产品的抽象&#xff1a; public interface IProduct {void Operation(); }然后&#xff0c;创建具体的产品类&#xff1a; public class ConcreteProductA : IProduct {public void Operat…

vue基础知识五:请描述下你对vue生命周期的理解?在created和mounted这两个生命周期中请求数据有什么区别呢?

一、生命周期是什么 生命周期&#xff08;Life Cycle&#xff09;的概念应用很广泛&#xff0c;特别是在政治、经济、环境、技术、社会等诸多领域经常出现&#xff0c;其基本涵义可以通俗地理解为“从摇篮到坟墓”&#xff08;Cradle-to-Grave&#xff09;的整个过程在Vue中实…

41 | 京东商家书籍评论数据分析

京东作为中国领先的电子商务平台,积累了大量商品评论数据,这些数据蕴含了丰富的信息。通过文本数据分析,我们可以了解用户对产品的态度、评价的关键词、消费者的需求等,从而有助于商家优化产品和服务,以及消费者作出更明智的购买决策。 本文将详细阐述如何获取京东商家评…

Python opennsfw/opennsfw2 图片/视频 鉴黄 笔记

nsfw&#xff08; Not Suitable for Work&#xff09;直接翻译就是 工作的时候不适合看&#xff0c;真文雅 nsfw效果&#xff0c;注意底部的分数 大体流程&#xff0c;输入图片/视频&#xff0c;输出0-1之间的数字&#xff0c;一般情况下&#xff0c;Scores < 0.2 认为是非…

7zip分卷压缩

前言 有些项目上传文件大小有限制 压缩包大了之后传输也会比较慢 解决方案 我们可以利用7zip压缩工具对文件进行分卷压缩 利用7zip压缩工具进行分卷压缩 查看待压缩文件大小 压缩完成之后有300多M&#xff0c;我们用100M去进行分卷压缩 选择待压缩的文件夹&#xff0c;右…

网络安全 Day30-运维安全项目-容器架构上

容器架构上 1. 什么是容器2. 容器 vs 虚拟机(化) :star::star:3. Docker极速上手指南1&#xff09;使用rpm包安装docker2) docker下载镜像加速的配置3) 载入镜像大礼包&#xff08;老师资料包中有&#xff09; 4. Docker使用案例1&#xff09; 案例01&#xff1a;:star::star::…

《内网穿透》无需公网IP,公网SSH远程访问家中的树莓派

文章目录 前言 如何通过 SSH 连接到树莓派步骤1. 在 Raspberry Pi 上启用 SSH步骤2. 查找树莓派的 IP 地址步骤3. SSH 到你的树莓派步骤 4. 在任何地点访问家中的树莓派4.1 安装 Cpolar内网穿透4.2 cpolar进行token认证4.3 配置cpolar服务开机自启动4.4 查看映射到公网的隧道地…