node.js安装教学(在Node.js中创建可恢复的上传器)
如果您曾经上传过相当大的视频文件,那么您就会知道这种感觉:您已经完成了 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 id="UploadBox">
<h2>Video Uploader</h2>
<span id="UploadArea">
<label for="FileBox">Choose A File: </label
><input type="file" id="FileBox" /><br />
<label for="NameBox">Name: </label
><input type="text" id="NameBox" /><br />
<button type="button" id="UploadButton" class="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 id="ProgressContainer"><div id="ProgressBar"></div></div><span id="percent">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 播放器配对时,您可以想象其可能性!
,免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com