Node.js 构建一个 Rust 应用图片处理应用
WebAssembly/wasm WebAssembly 或者 wasm 是一个可移植、体积小、加载快并且兼容 Web 的全新格式。
WebAssembly 可以被 JavaScript 调用,进入 JavaScript 上下文,也可以像 Web API 一样调用浏览器的功能。当然,WebAssembly 不仅可以运行在浏览器上,也可以运行在非web的沙箱环境下。
构建 wasm 的开发语言
目前wasm支持多种后端语言开发:
常用的C/C++、Golang、JS、Python等
可以参考 WebAssembly Awesome Github.
为什么使用rust
最主要原因是目前rust对于wasm有整套的运行编译链,更加完善的环境
引用 rust官网 的文字可以说明一下
1.高性能
Rust 速度惊人且内存利用率极高。由于没有运行时和垃圾回收,它能够胜任对性能要求特别高的服务,可以在嵌入式设备上运行,还能轻松和其他语言集成。
2.可靠性
Rust 丰富的类型系统和所有权模型保证了内存安全和线程安全,让您在编译期就能够消除各种各样的错误。
3.生产力
Rust 拥有出色的文档、友好的编译器和清晰的错误提示信息, 还集成了一流的工具——包管理器和构建工具, 智能地自动补全和类型检验的多编辑器支持, 以及自动格式化代码等等。
rust 安装与可编译生成的 wasm 目标 target
安装rust 参考官网的document即可
查看一下安装版本
$ rustc -V
rustc 1.46.0-nightly (feb3536eb 2020-06-09)
安装成功
通过执行
rustup target list
可以查看到rust支持的wasm类型
asmjs-unknown-emscripten
wasm32-unknown-emscripten
wasm32-unknown-unknown
wasm32-wasi
前3个主要编译用于前端web页面的js调用,没有IO操作。
本文主要讲解第4种 ,WASI 是 The WebAssembly System Interface 的缩写,简单来说就是一套接口标准,将 wasm 的应用领域从 web 中拓展到更广阔的各个平台中去(可以想像成类似 libc 之类的东西,但是是跨平台的)。
目前 wasi 的执行主要依靠安全可靠的运行时(可理解成沙箱或者虚拟机环境)环境执行,保证更加安全的数据,主要开发嵌入开发、区块链技术、人工智能等应用。
常用的运行时环境有wasmtime、wasmer、ssvm
在 SSVM 环境中开发一款图片处理应用
开发环境必备:
Ubuntu
docker
Koajs
开发流程图如下:
web端 Koa后端 rust生成的wasm 图片上传至后端 图片数据生成 uint8Array 图片处理后的base64码 前端渲染base64图片 web端 Koa后端 rust生成的wasm
具体代码实现
- web 页面实现
<form action="/api/image" method="post" enctype="multipart/form-data">
<input type="file" name="file" id="file" value=""/>
<input type="submit" value="提交"/>
</form>
form表单提交文件,action为后端接口
- Koa处理图片数据转成 uint8Array 类型
安装koa-body
处理参数
const koaBody = require('koa-body');
app.use(koaBody({
multipart: true,
formidable: {
maxFileSize: 200 * 1024 * 1024 // 设置上传文件大小最大限制,默认2M
}
}));
具体处理图片
if (ctx.url === '/api/image' && ctx.method === 'POST') {
let data = await fs.readFileSync(ctx.request.files.file.path)
let u8arr = new Uint8Array(data)
let data2 = get_image_gray(u8arr)
ctx.body = {
msg: `${data2}`
}
}
- rust后端处理
通过cargo
创建项目,在cargo.toml插入
[lib]
name = "ssvm_nodejs_starter_lib"
path = "src/lib.rs"
crate-type =["cdylib"]
[dependencies]
wasm-bindgen = "=0.2.61"
image = { version = "0.23.8", default-features = false, features = ["jpeg", "png"] }
base64 = "0.12.1"
具体图片处理使用image
以及base64
包
直接上代码
use wasm_bindgen::prelude::*;
extern crate image;
use base64::encode;
use std::io::{Cursor, Read, Seek, SeekFrom};
use std::vec::Vec;
// 加载图片获取图片信息
fn load_image_from_array(arr: &[u8]) -> image::DynamicImage {
let img = match image::load_from_memory_with_format(arr, image::ImageFormat::Png) {
Ok(img) => img,
Err(err) => {
panic!("format error, {:?}", err);
}
};
img
}
//将图片转成base64返回
fn get_base64_image(img: image::DynamicImage) -> String {
let mut c = Cursor::new(Vec::new());
match img.write_to(&mut c, image::ImageFormat::Png) {
Ok(c) => c,
Err(error) => panic!(
"There was a problem writing the resulting buffer: {:?}",
error
),
};
c.seek(SeekFrom::Start(0)).unwrap();
let mut out = Vec::new();
c.read_to_end(&mut out).unwrap();
let stt = encode(&mut out);
let together = format!("{}{}", "data:image/png;base64,", stt);
return together;
}
// 需要传到前端的处理图片函数
#[wasm_bindgen]
pub fn get_image_blur(data: &[u8]) -> String {
let mut image = load_image_from_array(data);
image = image.blur(2.0);// 将图片模糊处理、也可以缩放、灰度、对比度等处理
let base64_str = get_base64_image(image);
return base64_str;
}
参考源代码