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.pswp.cn/news/40397.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/40397.shtml
英文地址,請注明出處:http://en.pswp.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 查看映射到公網的隧道地…