🚀JS使用Wasm为你的文件MD5计算装上火箭引擎🚀

简介: 🚀JS使用Wasm为你的文件MD5计算装上火箭引擎🚀

前言

之前在一个自己的项目中尝试做一个web视频转码功能,计划用的是ffmpeg这个强大的库。当时就了解到了wasmffmpeg移植到浏览器中使用。但是等真正要发布到生产的时候还是遇到一些问题,

比如说ffmpeg体积比较大,加载速度缓慢;还有sharedArrayBufferffmpeg.wasm的一些关系,简单来说就是如果需要使用多线程版的ffmpeg,就需要设置COOP/COEP这两个新的跨域策略,但是设置这两个东西就会破坏OAuth的集成;或者可以选择使用单线程的ffmpeg,但是效率感人。。

但是这并不影响将C/C++/Rust等语言编译成Wasm移植到浏览器依旧是一种很有魅力的解决方案,今天一起来走进它吧!

Wasm简介

WebAssembly(Wasm)是一种开放标准,旨在提供一种可移植、高性能的二进制格式,用于在web浏览器中运行。它不是特定于任何语言的,而是为多种编程语言设计,包括C、C++、Rust等。通过将代码编译为Wasm格式,开发人员可以实现在不同平台和浏览器上运行的一致性性能。

Wasm的主要目标之一是提供比传统的JavaScript更高效的执行速度。它允许开发人员使用其他语言编写部分应用程序,然后将这些部分集成到web应用程序中,实现更好的性能和更广泛的语言选择。

此外,Wasm还提供了安全性、可移植性和版本控制等方面的优势。它在web浏览器中作为一个虚拟机执行,与浏览器的JavaScript引擎紧密集成,使得web应用程序可以更高效地利用底层硬件资源。

Hello Rust

Rust是一种系统级编程语言,注重内存安全、并发性和性能。由Mozilla开发,使用它可以高效地控制硬件,同时保持高级语言的安全性。具体有以下比较突出的特点:

  • 内存安全: Rust通过所有权系统、生命周期检查和借用机制,有效地防止了空指针引用、数据竞争和内存泄漏等内存安全问题,使得编写安全的并发代码更为容易。
  • 性能: Rust提供了接近底层语言(如CC++)的性能,同时保持了高级语言的抽象特性。零成本抽象的设计意味着你可以高效地控制硬件,而不会损失性能。
  • 并发性: Rust通过所有权系统和借用机制,支持并发编程,同时避免了常见的并发错误。这使得开发者能够编写线程安全的代码,而不需要额外的锁或同步原语。
  • 生态系统: Rust拥有一个不断壮大的生态系统,有丰富的库和工具,涵盖了各种应用场景。这使得开发者能够更容易地构建各种类型的应用,从系统级应用到Web服务。
  • 开发者友好: Rust的语法清晰、现代化,拥有友好的文档和社区支持。它鼓励编写易读易维护的代码,同时提供了丰富的工具链和调试支持。

Rust的具体安装方式可以参考这个文档:Rust安装,如果你是Mac用户,看到下图的时候表示Rust已经安装完毕:

211.png

安装Rust的时候一般情况下会自带安装cargo,它是Rust的库管理工具,类似于npm。我们可以使用 cargo new hello_rust来创建一个Rust项目。 333.png

项目安装好之后结构目录大致如上,如果你是使用vscode进行开发的话,建议安装rust-analyzer这个插件,它提供了代码的语法分析、自动完成、错误分析等功能,可以大大的提升我们的开发效率。

下面执行一下cargo run命令,就可以把我们的Rust项目跑起来: 212.png


计算文件MD5

首先使用cargo new 来创建一个Rust项目,在Cargo.toml中填入以下的内容

[package]
name = "rust_md5"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

[dependencies]
md-5 = "0.9.0"
js-sys = "0.3.50"
wasm-bindgen = "0.2.73"

[profile.release]
opt-level = 3
lto = true
strip = true
panic = "abort"

解释一下上面的字段:

  • package:包的相关信息
  • name:指定了你的项目的名称。每个 Rust 项目都有一个唯一的名称。
  • version:指定了你的项目的版本号。这遵循语义化版本规范(Semantic Versioning),通常包括主版本号、次版本号和修订号。
  • edition:指定了 Rust 编译器所使用的语言版本。在这里,它指定了项目使用 Rust 2021 Edition。
  • lib
  • crate-type:这里指定了生成的 crate 的类型为动态链接库(.cdylib),这通常用于构建 WebAssembly 模块。
  • dependencies 依赖项
  • [profile.release] :关于 release 模式的配置。
  • opt-level = 3:指定了编译器的优化级别。在 release 模式下,通常选择最高级别(3),以便进行更强大的优化。
  • lto = true:启用 Link Time Optimization(LTO),这允许在链接阶段进行更广泛的优化。
  • strip = true:启用在编译结束后去除调试信息和未使用的代码等优化,以减小生成的二进制文件的大小。
  • panic = "abort" :指定了在 release 模式下发生 panic 时的处理方式。这里设置为 "abort",表示在发生 panic 时立即终止程序。

然后在src文件夹下新增一个lib.rs文件,利用rustmd5库来计算文件的md5,其中输入是uint8数组,输出是一个字符串

use md5::{Digest, Md5};
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsValue;

#[wasm_bindgen]
#[repr(C)]
pub struct RustMd5 {
}

#[wasm_bindgen]
impl RustMd5 {
    pub fn new() -> Self {
        RustMd5 {
        }
    }

    pub fn calculate_md5(&self, file_buffer: &[u8]) -> Result<String, JsValue> {
        let mut md5 = Md5::new();
        md5.update(file_buffer);

        let result = md5.finalize();

        let md5_string = result
            .iter()
            .map(|b| format!("{:02x}", b))
            .collect::<String>();

        Ok(md5_string)
    }
}

打包Wasm

接下来我们就可以把这个rust工程来打包成wasm产物,使用到的是wasm-pack这个工具,首先可以使用cargo install wasm-pack来安装这个打包工具,然后执行wasm-pack build就可以开始打包。

323.png

打包出来的产物如下 343.png

Vite引入使用

打包好wasm模块之后,我们就可以将其引入到项目中使用了,这里我以vite搭建的工程为例,介绍如何把wasm模块引入到项目之中使用。Vite的配置文件如下,重点需要关注的是vite-plugin-wasmvite-plugin-top-level-await这两个包,记得提前安装好。

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import wasm from "vite-plugin-wasm";
import topLevelAwait from "vite-plugin-top-level-await";
export default defineConfig({
  plugins: [
    react(),
    wasm(),
    topLevelAwait()
  ],
});

然后把打包好的pkg文件夹放到前端项目下,用以下的方式引入:

import * as wasm from "./pkg/rust_md5.js";
console.log(wasm);
const rustMd5 = wasm.RustMd5.new();

444.png


接下来就可以上传文件并计算文件的MD5了,这里需要注意的是我们写的Rust模块计算MD5的方法接受的是一个Uint8Array,所以前端需要转换一下再传输给Rust,示例代码如下:

  const handleFileChange = (e) => {
    const uploadedFile = e.target.files[0];

    // 使用 FileReader 读取文件并转换为 ArrayBuffer
    const fileReader = new FileReader();
    console.log("读取文件并转换为 ArrayBuffer");
    fileReader.onload = function (e) {
      // 获取 ArrayBuffer
      const arrayBuffer = e.target.result;
      const startTime = performance.now();
      const uint8Array = new Uint8Array(arrayBuffer);
      const res = rustMd5.calculate_md5(uint8Array);
      console.log("res", res);
      const endTime = performance.now();
      const executionTime = (endTime - startTime) / 1000; // 单位:秒
      alert(executionTime + "s");
    };

    // 以 ArrayBuffer 格式读取文件
    fileReader.readAsArrayBuffer(uploadedFile);
  };
 
// 省略一些代码
<input type="file" onChange={handleFileChange} />

对比JS

我使用了几种规格的文件大小,分别对JS计算MD5Rust计算MD5的速度进行了对比,我的测试笔记本是Apple M1芯片,8G内存。

结果如下,单位为秒


文件大小 300K 1.5M 15M 125M 2G
Rust 0.0036 0.0040 0.032 0.2635 5.28
Js 0.0124 0.028 0.16 1.26 21.148

从上面可以看出,Rust无论在任何文件体积下,速度都比JS5倍左右,看到这个结果我不禁感慨,Rust竟恐怖如斯。

最后

本文以计算MD5为场景,介绍了Rust打包Wasm产物并引入到Vite中使用的一种方式,纯属抛砖引玉。如果你有其他想法,欢迎评论区或私信交流,如果觉得有趣的话,点点关注点点赞吧~


相关文章
|
21天前
|
JavaScript
浏览器插件crx文件--JS混淆与解密
浏览器插件crx文件--JS混淆与解密
21 0
|
3天前
|
JavaScript 前端开发 UED
在 JavaScript 中,异步编程和回调函数是处理非阻塞操作(如网络请求、文件读写等)的重要工具
【5月更文挑战第10天】JavaScript中的异步编程和回调函数用于处理非阻塞操作,提高应用响应性和吞吐量。异步编程通过回调函数、Promises和async/await等方式实现,避免程序因等待操作完成而阻塞。回调函数是异步操作完成后调用的函数,常用于处理网络请求等。然而,回调函数存在嵌套问题和错误处理困难,因此出现了Promises和async/await等更优解决方案。
10 3
|
3天前
|
JavaScript 前端开发 测试技术
编写JavaScript模块化代码主要涉及将代码分割成不同的文件或模块,每个模块负责处理特定的功能或任务
【5月更文挑战第10天】编写JavaScript模块化代码最佳实践:使用ES6模块或CommonJS(Node.js),组织逻辑相关模块,避免全局变量,封装细节。利用命名空间和目录结构,借助Webpack处理浏览器环境的模块。编写文档和注释,编写单元测试以确保代码质量。通过这些方法提升代码的可读性和可维护性。
9 3
|
11天前
|
JSON JavaScript 前端开发
使用JavaScript和XLSX.js将数据导出为Excel文件
使用JavaScript和XLSX.js将数据导出为Excel文件
21 0
|
15天前
|
运维 JavaScript Java
Serverless 应用引擎产品使用之阿里云Serverless函数计算中,在Node.js环境中执行jar文件如何解决
阿里云Serverless 应用引擎(SAE)提供了完整的微服务应用生命周期管理能力,包括应用部署、服务治理、开发运维、资源管理等功能,并通过扩展功能支持多环境管理、API Gateway、事件驱动等高级应用场景,帮助企业快速构建、部署、运维和扩展微服务架构,实现Serverless化的应用部署与运维模式。以下是对SAE产品使用合集的概述,包括应用管理、服务治理、开发运维、资源管理等方面。
21 0
|
1月前
|
JavaScript
【vue】vue2 导入外部js文件如何拿到方法的返回值
【vue】vue2 导入外部js文件如何拿到方法的返回值
17 1
|
1月前
|
小程序 开发工具 开发者
【微信小程序】微信开发者工具 引用 vant-weapp时“miniprogram/node_modules/@babel/runtime/index.js: 未找到npm包入口文件” 解决办法
【微信小程序】微信开发者工具 引用 vant-weapp时“miniprogram/node_modules/@babel/runtime/index.js: 未找到npm包入口文件” 解决办法
22 1
|
1月前
|
存储 JavaScript 前端开发
JavaScript中表格文件导出功能详解
JavaScript中表格文件导出功能详解
|
JavaScript 前端开发
利用Javascript实现简单计算
利用Javascript实现简单计算
利用Javascript实现简单计算
|
14天前
|
存储 移动开发 JavaScript
学习javascript,前端知识精讲,助力你轻松掌握
学习javascript,前端知识精讲,助力你轻松掌握