NodeJS解决方案不是那么表现,尤其是如果我们考虑使用一堆同步操作的解决方案,反之亦然 - 我们可以使用棘手的多线程解决方案。一个很好的例子是图像处理或密码。尽管有一些性能问题,但Nodejs仍保持其主流声誉。此外,Nodejs试图更灵活。功能强大的NodeJS Addons功能使开发人员可以在C ++上编写一些NodeJS功能。 Node.js with Rust上一次变得流行。我的意思是这种技术,因为我将与nodejs讨论生锈的编程语言集成。为什么要生锈?这是一个很好的问题...我想简要提供有关生锈的一些基本事实。
- 内存安全方法可防止内存泄漏。
- 类型安全语法控制。
- 由于并发管理,没有“数据竞赛”问题。
- 程序以“提前”方式编译。
- 利用并促进零成本的抽象。
- 没有资源的“垃圾收集器”,没有JIT编译器,没有虚拟机。
- 最小的运行时和内存足迹。
- 非常好的依赖性管理工具。
- 有用的编译器错误带有清晰可行的建议。
除此之外,Rust是多线程友好的,与C/C ++相比,它具有更简单的语法。
您可以找到following resource有价值的。 This resource会说服您关于生锈性能。
看到上面描述的生锈整合有点困难。幸运的是,进化并没有停止。今天,我很高兴向我们的技术动物园介绍一种新动物。
见Daily-Rs!
NAPI-RS是构建预编译节点的框架。
让我们跳下蝙蝠!
目的
当然,本文旨在将napi-rs
介绍为将nodej与Rust集成的最简单方法。最好的方法是提供一个比标准示例更复杂的例子。
我将提供一个nodejs应用程序,该应用程序获取文件,将其上传并随后将其转换。可以说,它正在减少饱和度。上面的图像操作应在生锈的一侧提供。
,但在此之前,让我们尝试标准功能。
包装模板
首先,您需要安装Rust。 Cargo
建造者包括那里。
其次,我建议通过以下template创建一个新项目。
第三,这里建议使用yarn
。
是时候涵盖所有要点了。
安装nodejs依赖关系并构建
yarn install
yarn build
生锈部分
Cargo.toml
包含有关生锈软件包的所有信息,包括依赖项。该文件类似于Nodejs中的package.json
。
src/lib.rs
上面的文件包含用于未来导出的Rust定义功能。在此示例中,定义的函数plus_100
将100添加到输入参数。
#![deny(clippy::all)]
use napi_derive::napi;
#[napi]
pub fn plus_100(input: u32) -> u32 {
input + 100
}
nodejs部分
很明显,在这里看到package.json
和其他JS的东西,因为我们在谈论Rust和Nodejs集成。 package.json
包含诸如@napi-rs/cli
之类的依赖项,使您可以构建解决方案。另外,请注意以下文件。
./index.js
此文件包含您的库与其导出。请查看代码的最后一行。
const { plus100 } = nativeBinding;
module.exports.plus100 = plus100;
您还记得上述Rust的plus100
定义吗?这些线
精确地表示Rust和Nodejs之间的桥梁。
./index.d.ts
此文件包含您的Rust功能的打字稿定义(签名)。
/* tslint:disable */
/* eslint-disable */
/* auto-generated by NAPI-RS */
export function plus100(input: number): number
重要说明!您不应该编辑上述文件,因为它们已自动化并在完成yarn build
命令后更改每个Rust定义更新。
./simple-test.js
以下代码说明了如何运行RUST定义的函数。注意第一行。您应该从./index.js
导入功能(请参见上文)。
const { plus100 } = require("./index");
console.assert(plus100(0) === 100, "Simple test failed");
console.info("Simple test passed");
让我们运行。
node simple-test
图像处理
我们确定您的解决方案效果很好,让我们对解决方案进行友好处理。让我们通过以下步骤。
更改./Cargo.toml
[lib]
crate-type = ["cdylib"]
path = "lib/lib.rs"
path = "lib/lib.rs"
已添加。现在,我们将lib
文件夹代替src
作为生锈代码。 src
文件夹可以保留用于未来的JS/TS代码。让我们暂时删除src
文件夹。
生锈的东西
首先,安装预期的生锈依赖性(image
软件包)。
cargo add image
第二,创建lib/lib.rs。
#![deny(clippy::all)]
use image::{GenericImageView, ImageBuffer, Pixel};
use napi_derive::napi;
#[napi]
pub fn darker(filename: String, saturation: u8) {
let img = image::open(filename.clone()).expect("File not found!");
let (w, h) = img.dimensions();
let mut output = ImageBuffer::new(w, h);
for (x, y, pixel) in img.pixels() {
output.put_pixel(x, y, pixel.map(|p| p.saturating_sub(saturation)));
}
output.save(filename).unwrap();
}
#[napi]
属性是一个标记,该功能应在JS/TS代码中使用。
上面的功能以文件名和饱和度,读取文件,应用饱和并重写文件。
让我们重建...
yarn build
因此,应更新index.js
和index.d.ts
。
将https://github.com/buchslava/napi-rs-images/blob/main/cube.png复制到项目的根源。
另外,让我们更改simple-test.js。
const { darker } = require("./index");
darker("./cube.png", 50);
是时候运行它了。
node simple-test
或在下面运行以下命令,如果要从一开始就重现所有步骤。
git clone git@github.com:buchslava/napi-rs-images.git
cd napi-rs-images
yarn
yarn build
node simple-test
查看以下更改。
我们的生锈部分已经准备就绪,是时候实施一个Web应用程序,该应用程序允许我们上传/删除文件并在后面显示。
如果您想立即尝试使用该应用程序,则可以使用https://github.com/buchslava/napi-rs-images玩。否则,请在下面阅读我的解释。
最后的针迹
首先,我们需要安装预期的nodejs依赖项。
yarn add ejs
yarn add express
yarn add express-ejs-layouts
yarn add express-fileupload
yarn add uuid
在项目的根部下制作storage
文件夹,然后将其添加到./.gitignore
。
将./server.js添加到项目的根源。
const fs = require("fs");
const path = require("path");
const express = require("express");
const ejsLayouts = require("express-ejs-layouts");
const fileUpload = require("express-fileupload");
const uuidv4 = require("uuid").v4;
const { darker } = require("./index");
const STORAGE_DIR = "storage";
const app = express();
app.use(fileUpload());
app.set("view engine", "ejs");
app.use(ejsLayouts);
app.use("/storage", express.static(path.join(__dirname, STORAGE_DIR)));
app.use(express.urlencoded({ extended: true }));
app.get("/", async (req, res) => {
let files = await fs.promises.readdir(path.join(__dirname, STORAGE_DIR));
files = files
.map((fileName) => ({
name: fileName,
time: fs
.statSync(path.join(__dirname, STORAGE_DIR, fileName))
.mtime.getTime(),
}))
.sort((a, b) => a.time - b.time)
.map((v) => v.name);
return res.render("upload", { files: files.reverse() });
});
app.post("/uploads", function (req, res) {
const file = req.files.upload;
const extname = path.extname(file.name);
const uuid = uuidv4();
const filePath = path.join(__dirname, STORAGE_DIR, `${uuid}${extname}`);
file.mv(filePath, (err) => {
if (err) {
return res.status(500).send(err);
}
try {
darker(filePath, +req.body.saturation);
} catch (e) {
return res.status(500).send(e);
}
res.redirect("/");
});
});
app.listen(3000);
另外,将"start": "node server",
添加到./package.json
的scripts
部分。
我不想解释上面的许多解决方案,因为这对于Nodejs的人来说是显而易见的。我只想注意以下要点。
- 有两个端点:
/
和/upload
。 -
/
为我们提供了上传表格和上传和去饱和图像的列表。 -
/upload
上传并保留上载的图像并重定向到/
。
另外,请查看图像删节
try {
darker(filePath, +req.body.saturation);
} catch (e) {
return res.status(500).send(e);
}
我们从请求+req.body.saturation
中获取饱和值的事实,
let files = await fs.promises.readdir(path.join(__dirname, STORAGE_DIR));
files = files
.map((fileName) => ({
name: fileName,
time: fs
.statSync(path.join(__dirname, STORAGE_DIR, fileName))
.mtime.getTime(),
}))
.sort((a, b) => a.time - b.time)
.map((v) => v.name);
return res.render("upload", { files: files.reverse() });
workat_dir是storage
(见上文),我们将上传文件的排序列表传递给相关的EJS模板。
相关的EJS模板在下面。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
crossorigin="anonymous"
/>
<title>Uploads</title>
</head>
<body>
<%- body %>
</body>
</html>
<div class="container">
<form
class="w-50 mx-auto my-3"
action="/uploads"
method="post"
enctype="multipart/form-data"
>
<div class="mb-3">
<input class="form-control" type="file" name="upload" required />
</div>
<div class="w-50 d-flex form-outline align-middle">
<label class="form-label text-nowrap pr-3" for="saturation"
>% saturation </label
>
<input
name="saturation"
value="65"
type="number"
id="saturation"
class="form-control"
/>
</div>
<button class="btn btn-primary">Upload</button>
</form>
<div class="container">
<% for (const file of files){ %>
<div class="row mb-3">
<img src="/storage/<%= file %>" class="card-img-top" alt="Image" />
</div>
<% } %>
</div>
</div>
是时候测试整个解决方案了。
yarn start
尝试http://localhost:3000
最后,让我们上传几个图像。
和另一个
我想如果您上传和处理更大的图像,您会满足您对性能的好奇心。
总而言之,我想指的是here的事实。
“一个不错的功能是,该板条箱允许您纯粹使用Rust/JavaScript工具链构建附加组件,而无需涉及Node-GYP。”
那样的音乐喜欢节点的人的耳朵。