科百科
当前位置: 首页 范文大全

node.js安装教学(在Node.js中创建可恢复的上传器)

时间:2023-06-11 作者: 小编 阅读量: 1 栏目名: 范文大全

为了完成这项任务,我们将完全控制Node.js服务器以请求特定的数据块,HTML表单将接收这些请求并将必要的信息发送到服务器。为了处理这种通信,我们将使用Socket.io。该类FileReader允许我们打开和读取文件的一部分,并将数据作为二进制字符串传递给服务器。这是通过WebSocket协议完成的,该协议是一种与客户端和服务器建立极快的双向连接的方法。现在,我们可以回到HTML文件并定义一些Socket.io事件。

如果您曾经上传过相当大的视频文件,那么您就会知道这种感觉:您已经完成了 90%,但不小心刷新了页面——不得不重新开始。

在本教程中,我将演示如何为您的网站制作一个可以恢复中断的上传并在完成后生成缩略图的视频上传器。

介绍

为了使这个上传器可以恢复,服务器需要跟踪一个文件已经上传了多少,并且能够从它停止的地方继续。为了完成这项任务,我们将完全控制 Node.js 服务器以请求特定的数据块,HTML 表单将接收这些请求并将必要的信息发送到服务器。

为了处理这种通信,我们将使用 Socket.io。如果您从未听说过 Socket.io,它是一个用于在 Node.js 和 HTML 网页之间进行实时通信的框架——我们很快就会对此进行深入研究。

这是基本概念;我们将从 HTML 表单开始。

1.创建 HTML

我将保持 HTML 相当简单;我们需要的只是一个选择文件的输入、一个名称的文本框和一个开始上传的按钮。这是必要的代码:

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8" />

<meta http-equiv="X-UA-Compatible" content="IE=edge" />

<meta name="viewport" content="width=device-width, initial-scale=1.0" />

<title>Resumable Video Uploader</title>

</head>

<body>

<div >

<h2>Video Uploader</h2>

<span >

<label for="FileBox">Choose A File: </label

><input type="file"/><br />

<label for="NameBox">Name: </label

><input type="text"/><br />

<button type="button">Upload</button>

</span>

</div>

</body>

<script type="module">

// put JavaScript code here

</script>

</html>

请注意,我已将内容包装在一个跨度中;稍后我们将使用它来使用 JavaScript 更新页面的布局。我不会在本教程中介绍 CSS,但如果您想使用我的,可以下载源代码。

2.FileReader使用API添加上传按钮

在本教程中,我们将使用FileReader几乎普遍支持的 HTML5 API。

该类FileReader允许我们打开和读取文件的一部分,并将数据作为二进制字符串传递给服务器。这是用于特征检测的 JavaScript:

document.getElementById('UploadButton').addEventListener('click', StartUpload);

document.getElementById('FileBox').addEventListener('change', FileChosen);

上面的代码还向表单中的按钮和文件输入添加了事件处理程序。该FileChosen函数简单地为文件设置一个全局变量——以便我们以后可以访问它——并填写该name字段,以便用户在命名文件时有一个参考点。这是FileChosen功能:

let SelectedFile;

function FileChosen(evnt) {

SelectedFile = evnt.target.files[0];

document.getElementById("NameBox").value = SelectedFile.name;

}

在我们编写StartUpload函数之前,我们必须使用 Socket.io 设置 Node.js 服务器;现在让我们处理一下。

3.创建 Socket.io 服务器

正如我之前提到的,我将使用 Socket.io 在服务器和 HTML 文件之间进行通信。要下载 Socket.io,npm install socket.io请在导航到此项目目录后,在终端窗口中键入(假设您已经安装了 Node.js)。Socket.io 的工作方式是:服务器或客户端“发出”一个事件,然后另一方将以函数的形式接收该事件,并可选择来回传递 JSON 数据。这是通过 WebSocket 协议完成的,该协议是一种与客户端和服务器建立极快的双向连接的方法。使用 WebSockets 的其他一些替代方法是多部分 HTTP 上传和 WebRTC。但是,我们在本教程中只使用 WebSockets。

首先,创建一个空的 JavaScript 文件,并将以下代码放入其中。

const app = require("http").createServer(handler),

{ Server } = require("socket.io"),

fs = require("fs"),

exec = require("child_process").exec,

util = require("util");

const io = new Server(app);

app.listen(8080);

function handler(req, res) {

fs.readFile(__dirname"/index.html", function (err, data) {

if (err) {

res.writeHead(500);

return res.end("Error loading index.html");

}

res.writeHead(200);

res.end(data);

});

}

io.on("connection", function (socket) {

//Events will go here

});

前五行包含所需的库,下一行指示服务器在端口 8080 上侦听,当用户访问该站点时,处理函数只是将我们的 HTML 文件的内容传递给用户。

最后两行是 Socket.io 处理程序,将在有人通过 Socket.io 连接时调用。

现在,我们可以回到 HTML 文件并定义一些 Socket.io 事件。

广告

4.发出一些 Socket.io 事件

要开始在我们的页面中使用 Socket.io,我们首先需要导入它的 JavaScript 库。我们将使用 ESM(浏览器中的新模块规范)来完成此操作。要使用 ESM 导入 Socket.io,我们将在我们正在运行的脚本的开头使用它:

import { io } from "https://cdn.socket.io/4.4.1/socket.io.esm.min.js";

如果您使用的是捆绑器,您也可以socket.io-client从那里安装和导入它。但是,我们不会在本教程中这样做。

现在,我们可以编写StartUpload连接到按钮的函数:

let FReader;

let Name;

function StartUpload() {

if (document.getElementById("FileBox").value != "") {

FReader = new FileReader();

Name = document.getElementById("NameBox").value;

let Content =

"<span id='NameArea'>Uploading "

SelectedFile.name

" as "

Name

"</span>";

Content=

'<div ><div ></div></div><span >0%</span>';

Content=

"<span id='Uploaded'> - <span id='MB'>0</span>/"

Math.round(SelectedFile.size / 1048576)

"MB</span>";

document.getElementById("UploadArea").innerHTML = Content;

FReader.onload = function (evnt) {

socket.emit("Upload", { Name: Name, Data: evnt.target.result });

};

socket.emit("Start", { Name: Name, Size: SelectedFile.size });

} else {

alert("Please Select A File");

}

}

第一行连接到 Socket.io 服务器;接下来,我们为文件读取器和文件名创建了两个变量,因为我们需要对这些变量进行全局访问。在函数内部,我们首先确保用户选择了一个文件,如果他们选择了,我们创建FileReader并使用一个漂亮的进度条更新 DOM。

FileReader 的onload方法在每次读取一些数据时被调用;我们需要做的就是发出一个Upload事件,并将数据发送到服务器。最后,我们发出一个Start事件,将文件的名称和大小传递给 Node.js 服务器。

现在,让我们回到 Node.js 文件,并为这两个事件实现处理程序。

5.处理服务器上的事件

您必须经常清除缓冲区,否则服务器将由于内存过载而崩溃。

socket.io 事件进入我们在 Node.js 文件最后一行的处理程序中。我们要实现的第一个事件是事件,它在用户单击上传按钮Start时触发。

我之前提到过,服务器应该控制它接下来要接收哪些数据;这将允许它从以前不完整的上传继续。它首先确定是否有一个同名的文件没有完成上传,如果是,它将从中断的地方继续;否则,它将从头开始。我们将以半兆字节为增量传递此数据,即 524288 字节。

为了跟踪同时发生的不同上传,我们需要添加一个变量来存储所有内容。在文件的顶部,添加let Files = {};'以下是Start事件的代码:

socket.on("Start", function (data) {

//data contains the variables that we passed through in the html file

const Name = data["Name"];

Files[Name] = {

//Create a new Entry in The Files Variable

FileSize: data["Size"],

Data: "",

Downloaded: 0,

};

let Place = 0;

try {

const Stat = fs.statSync("Temp/"Name);

if (Stat.isFile()) {

Files[Name]["Downloaded"] = Stat.size;

Place = Stat.size / 524288;

}

} catch (er) {} //It's a New File

fs.open("Temp/"Name, "a", 0755, function (err, fd) {

if (err) {

console.log(err);

} else {

Files[Name]["Handler"] = fd; //We store the file handler so we can write to it later

socket.emit("MoreData", { Place: Place, Percent: 0 });

}

});

});

首先,我们将新文件添加到Files数组中,其中包含到目前为止下载的大小、数据和字节数。Place变量存储我们在文件中的位置——它默认为 0,即开始。然后我们检查文件是否已经存在(即它在中间并停止),并相应地更新变量。无论是否是新上传,我们现在打开文件以写入Temp/文件夹,并发出MoreData事件以从 HTML 文件中请求下一部分数据。

现在,我们需要添加Upload事件,如果您还记得的话,每次读取新数据块时都会调用该事件。这是功能:

socket.on("Upload", function (data) {

var Name = data["Name"];

Files[Name]["Downloaded"]= data["Data"].length;

Files[Name]["Data"]= data["Data"];

if (Files[Name]["Downloaded"] == Files[Name]["FileSize"]) {

//If File is Fully Uploaded

fs.write(

Files[Name]["Handler"],

Files[Name]["Data"],

null,

"Binary",

function (err, Writen) {

//Get Thumbnail Here

}

);

} else if (Files[Name]["Data"].length > 10485760) {

//If the Data Buffer reaches 10MB

fs.write(

Files[Name]["Handler"],

Files[Name]["Data"],

null,

"Binary",

function (err, Writen) {

Files[Name]["Data"] = ""; //Reset The Buffer

let Place = Files[Name]["Downloaded"] / 524288;

let Percent =

(Files[Name]["Downloaded"] / Files[Name]["FileSize"]) * 100;

socket.emit("MoreData", { Place: Place, Percent: Percent });

}

);

} else {

let Place = Files[Name]["Downloaded"] / 524288;

let Percent = (Files[Name]["Downloaded"] / Files[Name]["FileSize"]) * 100;

socket.emit("MoreData", { Place: Place, Percent: Percent });

}

});

此代码的前两行使用新数据更新缓冲区,并更新下载的总字节数变量。我们必须将数据存储在缓冲区中并以增量方式保存,以免由于内存过载而导致服务器崩溃;每十兆字节,我们将保存并清除缓冲区。

第一条if语句确定文件是否已完全上传,第二条语句检查缓冲区是否已达到 10 MB,最后,我们请求MoreData,传入完成百分比和要获取的下一个数据块。

现在,我们可以回到 HTML 文件并实现MoreData事件并更新进度。

6.跟踪进度

我创建了一个函数来更新进度条和页面上上传的 MB 量。除此之外,该More Data事件还读取服务器请求的数据块,并将其传递给服务器。

要将文件分割成块,我们使用 File API 的Slice命令。由于 File API 仍在开发中,我们需要分别使用webkitSlice和mozSlice用于 Webkit 和 Mozilla 浏览器。

有了这个最终功能,上传器就完成了!我们剩下要做的就是将完成的文件移出Temp/文件夹并生成缩略图。

7.创建缩略图

在生成缩略图之前,我们需要将文件移出临时文件夹。我们可以通过使用文件流和pump方法来做到这一点。该pump方法接受一个读写流,并缓冲数据。您应该将此代码添加到我在事件中编写“在此处生成缩略图”的位置Upload:

let inp = fs.createReadStream("Temp/"Name);

let out = fs.createWriteStream("Video/"Name);

util.pump(inp, out, function () {

fs.unlink("Temp/"Name, function () {

//This Deletes The Temporary File

//Moving File Completed

});

});

我们添加了 unlink 命令;这将在我们完成复制后删除临时文件。现在进入缩略图:我们将使用 ffmpeg 生成缩略图,因为它可以处理多种格式,并且安装起来很容易。在撰写本文时,还没有任何好的 ffmpeg 模块,因此我们将使用该exec命令,它允许我们从 Node.js 中执行终端命令。有一个很有前途的ffmpeg WASM 端口,将来可能会有意义。如果您没有安装 ffmpeg,请查看ffmpeg 的下载页面。

exec(

"ffmpeg -i Video/"

Name

" -ss 01:30 -r 1 -an -vframes 1 -f mjpeg Video/"

Name

".jpg",

function (err) {

socket.emit("Done", { Image: "Video/"Name".jpg" });

}

此 ffmpeg 命令将在 1:30 标记处生成一个缩略图,并将其以.jpg文件类型保存到Video/文件夹。您可以通过更改参数来编辑缩略图的时间。一旦生成了缩略图,我们就会发出事件。现在,让我们回到 HTML 页面并实现它。-ssDone

8.完成

该Done事件将删除进度条并将其替换为缩略图。因为我们没有设置为静态文件服务器,所以您必须将服务器的位置(例如 Apache)放在Path变量中或配置服务器以提供图像以加载图像。

let Path = "https://localhost/";

socket.on("Done", function (data) {

const Content = "Video Successfully Uploaded !!";

Content=

"<img id='Thumb' src='"

Path

data["Image"]

"' alt='"

Name

"'><br>";

Content=

"<button type='button' name='Upload' value='' id='Restart' class='Button'>Upload Another</button>";

document.getElementById("UploadArea").innerHTML = Content;

document.getElementById("Restart").addEventListener("click", Refresh);

});

上面,我们添加了一个按钮来开始上传另一个文件;所有这一切都是刷新页面。

结论

这就是它的全部内容,但是,当然,当您将它与数据库和 HTML5 播放器配对时,您可以想象其可能性!

    推荐阅读
  • 桃花开来映山红具体是什么动物(桃红枊绿映山红是什么生肖)

    我国地域广阔,地势和地形多样,气候多变,部分地区有许多不同品种的蛇类生存。

  • 桃花源记翻译(这里有完整的译文)

    桃花源记翻译东晋太元年间,武陵有个人以捕鱼为生。人们在田间来来往往耕种劳动,男女的穿戴全都与桃花源以外的人一样。他到了郡城武陵,就去拜见太守,说了自己的这番经历。太守立即派遣人员跟随他前往,寻找渔人先前作的记号,最终迷路了,后来再也找不到通往桃花源的路了。但没有实现,不久后就病死了,后来就再也没有探访桃花源的人了。

  • 现在才知道自己的发际线这么高(这么露也能美出新高度)

    范冰冰的发际线是圆形发际线,额头处比较的平直,加上额头比较短,全部露出会显得脸的上部比例偏短。唐嫣也是高额头高发际线的类型,但是,她偏分露额的小心机却能让人忽视她的发际线问题。额头偏低的陈妍希在额前留几缕头发,不仅能掩盖发际线过低问题,看起来脸简直小了一大圈!范冰冰扎起丸子头后,在额头两侧留些头发,让本来直直的发际线呈现了一个好看的弧度。

  • 三年沃尔沃xc60值得买么(20万购5年的沃尔沃XC60)

    作为一个高度发达、高福利、高生活质量的国家,汽车自然要强调人性化,这也是沃尔沃汽车以“安全”著称的原因。前端梯形格栅上加大的铁标能够迅速充满前车的后视镜,明确昭示着沃尔沃汽车的未来。从侧面看,全新沃尔沃XC60雕塑般的魅力线条尤其明显。汽车肩部异常的宽大。刹车油笔的五个灯全亮,说明含水量严重超标,建议立即更换。此外,如果长时间不更换,可能会腐蚀制动系统,给行车带来一定的安全隐患。

  • 葡萄干怎么洗才干净(葡萄干要怎么清洗干净)

    葡萄干怎么洗才干净首先,准备一个干净的碗,然后往里面加入少许淀粉。然后,加入大半碗清水,然后搅拌均匀。然后,将葡萄干放进去浸泡3分钟左右。最后,将浸泡过葡萄干的水倒掉,然后再用清水冲洗几遍即可。平常我们买回来葡萄干都不洗就直接吃,这样是很脏的,在制作葡萄干的过程中空气中的灰尘会粘到上面,还有在人们挑选的过程中也会有脏东西,所以买回来的葡萄干不能直接吃,要用上面的方法仔细清洗干净才行。

  • 椒盐牛排如何做(怎么做椒盐牛排)

    以下内容希望对你有帮助!椒盐牛排如何做食材:牛排500克,绍酒、花椒盐、酱油、味精、干淀粉少许,鸡蛋一个,猪油适量。将牛排斩成宽、厚均匀的条,每条都要带上骨头。将牛骨条加入绍酒、酱油、味精、干淀粉和鸡蛋液煨味。全部投入八成热的油锅后用漏勺翻动,炸至呈金黄、浮起,捞出装盘,佐以花椒盐即可。

  • 鞠婧祎夏日装扮(鞠婧祎本周快本两套造型)

    虽然鞠婧祎在很多人印象中都不高,是个典型的小个子女生,但她由于身材比例好,在加上衣品高,所以每次在镜头中都不显矮。就像这次,鞠婧祎的淡黄裙裙长只是堪堪能遮住臀部,最大程度上拉长了腿部线条,修长纤细的美腿瞬间就出来了。网上有很多关于她整容的消息,但不敢有没有,现在的鞠婧祎确实很美,尤其是侧面看,五官立体精致,几乎毫无瑕疵。

  • 柯南和服部平次一起破案在哪一集(你都看了吗?)

    柯南和服部平次一起破案在哪一集?跟着小编一起来看一看吧!红白黄色与侦探团怪盗KID的瞬间移动魔术杀人犯-工藤新一

  • 80年代下海经商励志电视剧(三十年前一部魔性电视剧公关小姐)

    魔性二、另一个世界高楼大厦、五星级酒店、出租车、电脑电话,《公关小姐》在全国人民面前展现了一个全新的广州,那是一个和很多城市完全不一样的地方。其实说起来,当年这些公关小姐不光是外形靓丽,她们身上的那种不一样的气质和生活状态同样具有相当魅力。在《公关小姐》热播之后,全国各地各企业的公共关系部门有如雨后春笋一样建立起来。

  • 基本初等函数的性质是什么(基本初等函数的性质具体是什么)

    初等函数是由基本初等函数经过有限次的四则运算和复合运算所得到的函数。基本初等函数和初等函数在其定义区间内均为连续函数。不是初等函数的函数,称为非初等函数,如狄利克雷函数和黎曼函数。目前有两种分类方法:数学分析有六种基本初等函数,高等数学只有五种。