当前位置:网站首页>使用WebAssembly在浏览器端操作Excel
使用WebAssembly在浏览器端操作Excel
2022-07-05 20:49:00 【你的薄荷醇】
WebAssembly其实出现已经好多年了,是在浏览器端虚拟环境中运行汇编语言的一个方式,它编写的程序叫做wasm。WebAssembly 于 2015 年首次发布,主要的目标是让浏览器执行的速度追赶上原生应用程序,目前绝大多数浏览器都开始默认启用 WebAssembly。
编写WebAssembly,可以用很多语言,比如C,C++,RUST,.NET 等语言
下面我将演示用C++开发一个WebAssembly程序的完整过程。
开发系统环境:ubuntu16
一、WebAssembly开发过程
1.开发环境安装
虽然WebAssembly可以用C++开发,但这并不意味着,使用GCC,或者cl等编译器编译出的程序,直接可以在浏览器中运行,要把C/C++或者其它语言编译为wasm文件,需要使用其它工具进行翻译。目前最标准的使用工具就是emscripten
emscripten安装
emscripten这个东西,它对运行环境的要求很严格,所以,安装过程要严格按照官方提供的方式操作
我的开发环境是ubuntu,在ubuntu上安装emscripten,首先你要安装git,你的系统有没有安装过git,在终端上,执行一下就行
git
如果安装过,输出的内容应该和下图输出的差不多
如果没有的话,就安装一个。虽然emscrpten也可以在github上下载,但还是建议安装.输入以下命令,等待安装就可以
sudo apt-get install git
安装完git以后,就可以按照官方安装教程一步步的来了
# 从git下载emsdk
git clone https://github.com/emscripten-core/emsdk.git
# 进入emsdk目录
cd emsdk
# 合并本地代码(第一次下载的话,不需要)
git pull
# 下载和站桩最新版本
./emsdk install latest
#把最新版本设置为活动版本
./emsdk activate latest
# 设置环境变量
source ./emsdk_env.sh
上面这些步骤很重要,因为目前可以参考的资料较少,最好按照上面的步骤执行,如果某一步执行错误的话,要多次执行,保证每一步都执行完成
执行完成后在emsdk文件夹中的内容,大概如下
然后验证一下安装好了没有
emcc -v
输出以下内容就代表装好了
python安装
python是编译WebAssembly最重要的工具
目前emscripten版本所需要的python,是需要python3.6以上,如果你安装3.6以下版本,编译会失败。
如果你用的ubuntu也是16,那么系统内置的是python2,不能使用现在emscripten,要安装python3.6以上的版本
python3.6版本的安装建议使用源码安装,因为通过apt-get,我没有安装成功
去python官方网站下载
https://www.python.org/downloads/source/
我下载的是3.6.9 如果你没有把握,也和我下载一样的版本,然后执行解压
tar -xvzf Python-3.6.9.tg
解压完成后,进入Python-3.6.9文件夹,然后执行
./configure
然后执行
make
make install
这样就开始安装python3.6了
安装完成后,删除原来的python2链接
rm -rf /usr/bin/python
然后吧python3作为默认链接
ln -s (你的py3安装地址) /usr/bin/python
都操作完成后,直接敲入python,看看操作对了没有
如果你不知道刚才python3安装到哪,执行以下命令查询,然后再执行以上步骤
whereis python3
cmake安装
emscripten可以执行cmake编译,如果你要编译点小东西玩,就不需要了,但还是建议安装,如果你make用的熟的话,也可以使用make
可以看看自己安装过cmake没有,执行如下命令
cmake
如果安装过,大概输出就像上面这样
如果没有,就apt-get安装
apt-get install cmake
chrome浏览器
这个倒也不是必须的,除了chrome浏览器,edge等chrome内核浏览器也行,firefox也行,但是你安装的chrome,起码要75以后的版本才可以
以上东西都安装后,基本开发环境就完成了
2.用WebAssembly操作excel
下面来写个WebAssembly操作excel文件的demo
完整的代码可以到以下地址获取
https://github.com/ltframe/code/tree/main/wasm-excel-demo
操作execl.我采用的是C++编写的BasicExcel库,采用这个库的原因是,它虽然没有很强大功能,但足够小巧,使用简单,而且文件少,基本表格操作具备。我以前写过关于它的使用,可以去翻翻
实现的目标
1.读出内容,然后在C++中生成Json,输出到前端,再由前端程序显示出来
2.在前端修改内容,把修改后的json传回C++,然后生成新的文件,下载到浏览器
BasicExcel只能打开xls文件,xlsx不行
创建excel文档
创建一个excel文件,里面弄两个工作表,
然后在第一个工作表里输入内容
第二个工作表里也随便输入一点
编写C++文件
class myclass
{
public:
wstring outputstr = L"";
myclass()
{
}
void saveexcel(string value)
{
cJSON *json = cJSON_Parse(value.c_str());
cJSON *info = cJSON_GetObjectItem(json, "info");
cJSON *count = cJSON_GetObjectItem(info, "count");
cJSON *tablenames = cJSON_GetObjectItem(info, "tablenames");
int _count = count->valueint;
cJSON *data = cJSON_GetObjectItem(json, "data");
BasicExcel *e = new BasicExcel();
e->New(_count);
for (int i = 0; i < 1; i++)
{
BasicExcelWorksheet* sheet = e->GetWorksheet((unsigned)i);
cJSON *name = cJSON_GetArrayItem(tablenames, i);
sheet->Rename(StringToWString(name->valuestring).c_str());
cJSON *list = cJSON_GetObjectItem(data, name->valuestring);
for (int j = 0; j < cJSON_GetArraySize(list); j++)
{
cJSON *item = cJSON_GetArrayItem(list,j);
for (size_t k = 0; k < cJSON_GetArraySize(item); k++)
{
cJSON *c = cJSON_GetArrayItem(item, k);
BasicExcelCell* cell = sheet->Cell(j, k);
cell->SetWString(StringToWString(c->valuestring).c_str());
}
}
}
e->SaveAs("/data/demo.xls");
}
void readsheet(BasicExcelWorksheet* sheet1)
{
size_t rows = sheet1->GetTotalRows();
size_t cols = sheet1->GetTotalCols();
for (size_t r = 0; r < rows; r++) {
outputstr+=L"[";
for (size_t c = 0; c < cols; c++)
{
BasicExcelCell* cell = sheet1->Cell(r, c);
//判断单元格类型
switch (cell->Type())
{
case BasicExcelCell::WSTRING:
{
const wchar_t* ret = cell->GetWString();
outputstr += L"\"";
outputstr += ret;
outputstr += L"\"";
if (c != cols - 1) {
outputstr += L",";
}
}
break;
default:
break;
}
}
outputstr+=L"]";
if(r!=rows-1){
outputstr+=L",";
}
}
}
wstring loadexcel(string fb)
{
outputstr += L"{\"info\":{";
BasicExcel *e =new BasicExcel;
wprintf(L"%ls\r\n",L"start load...");
string fname = string("/data/")+fb;
if (!e->Load(fname.c_str()))
{
wprintf(L"%ls\r\n",L"load failed");
return wstring(L"load failed\n");
}
int sheetscount = e->GetTotalWorkSheets();
outputstr +=L"\"count\":"+ std::to_wstring(sheetscount);
outputstr+=L",\"tablenames\":[";
for (size_t i = 0; i < sheetscount; i++)
{
outputstr+=L"\"";
outputstr+=e->GetUnicodeSheetName(i);
outputstr+=L"\"";
if(i!=sheetscount-1){
outputstr+=L",";
}
}
outputstr+=L"]},\"data\":{";
for (size_t i = 0; i < sheetscount; i++) {
BasicExcelWorksheet* sheet1 = e->GetWorksheet(i);
if (sheet1) {
outputstr += L"\"";
outputstr += e->GetUnicodeSheetName(i);
outputstr += L"\":[";
readsheet(sheet1);
outputstr += L"]";
if(i!=sheetscount-1){
outputstr += L",";
}
}
}
outputstr += L"}}";
wprintf(L"%ls",outputstr.c_str());
return outputstr;
}
};
EMSCRIPTEN_BINDINGS(myclass) {
class_<myclass>("myclass")
.constructor()
.function("loadexcel", &myclass::loadexcel)
.function("saveexcel", &myclass::saveexcel)
;
}
void setup_IDBFS(){
EM_ASM(
FS.mkdir('/data');
FS.mount(IDBFS,{
},'/data');
);
}
int main()
{
setup_IDBFS();
setlocale(LC_ALL, "chs");
return 0;
}
BasicExcel使用就不说了,想了解的话,可以去看我以前写的文章。
主要说说emscripten相关部分
EMSCRIPTEN_BINDINGS的作用是建立一个C++类和JS类之间的绑定,绑定后,在前台js代码里,可以像使用一个js类一样使用它
function(“loadexcel”, &myclass::loadexcel),意思是,导出这个函数给前台JS使用,第一个参数,是在JS里调用方法的名称,第二个参数就是你的C++成员函数引用
EM_ASM(
FS.mkdir('/data');
FS.mount(IDBFS,{
},'/data');
);
这段代码表示创建一个虚拟的data文件夹,然后把他挂载到IDBFS系统中,以后对excel文件的操作都在这个文件夹中
setup_IDBFS函数的作用,是建立一个文件系统,采用IDBFS方式挂载虚拟文件的路径。因为WebAssembly终究还是运行在一个虚拟系统中,不可能访问你的硬盘系统,所以setup_IDBFS,意思就建立了一个JS的文件系统和你的C++代码文件系统的桥梁。
除了IDBFS,还有其它文件系统,每种文件系统的使用场景是不同的。可以自己去看看教程
编写cmake
CMAKE_MINIMUM_REQUIRED(VERSION 3.5)
PROJECT(cx)
AUX_SOURCE_DIRECTORY(. DIR_SOURCE)
SET(CMAKE_BUILD_TYPE "Debug")
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
SET(CMAKE_C_COMPILER emcc)
SET(CMAKE_CPP_COMPILER 'em++')
set(CMAKE_CXX_FLAGS "--bind")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s WASM=1 -s EXTRA_EXPORTED_RUNTIME_METHODS='[FS]' -s INITIAL_MEMORY=268435456 \
-s ASSERTIONS=1 \
-s STACK_OVERFLOW_CHECK=2 \
-s PTHREAD_POOL_SIZE_STRICT=2 \
-s ALLOW_MEMORY_GROWTH=1 \
-lidbfs.js")
ADD_EXECUTABLE(app ${DIR_SOURCE})
如果对CMAKE不太了解的话,可以先看看文档,简单了解一下cmake.
set(CMAKE_CXX_FLAGS "--bind")
表示要启用绑定功能,就是c++里写的EMSCRIPTEN_BINDINGS
CMAKE_EXE_LINKER_FLAGS 选项中lidbfs.js,表示要使用IDBFS文件方式
EXTRA_EXPORTED_RUNTIME_METHODS表示要导出FS文件系统前台使用
这样,C++部分就算完成了,就开始编译了,
在C++项目文件夹中执行如下命令,
mkdir build
进入build文件夹,执行
emcmake cmake ..
输出大概如上图
执行完成后,就出现了app.js app.wasm两个文件,这就是你的c++编译后的文件,app.wasm是c++编译后的文件,app.js是一个类似胶水的文件,把你的wasm和Js文件关联在一起,当然没有这个也可以,你也可以自己写调用模块
编写javascript
应为这个比较简单,本段只贴出操作的核心部分。其它部分省略
function writefile(e){
let result=reader.result;
const uint8_view = new Uint8Array(result);
FS.writeFile('/data/'+file.name, uint8_view)
}
function savetodisk()
{
var data=FS.readFile("/data/demo.xls");
var blob;
blob = new Blob([data.buffer], {
type: "application/vnd.ms-excel"});
saveAs(blob, "demo.xls");
}
function myfun(e) {
let files = document.getElementById('file').files;
file=files[0];
reader.addEventListener('loadend', writefile);
reader.readAsArrayBuffer(file);
setTimeout(()=>{
let jsonstr = instance.loadexcel(file.name);
//code......
},1000)
}
writefile函数的功能,是打开本地的excel文件,在虚拟的路径/data/当中,创建一个文件,这样C++里就能读取到它了,这里的data就是C++文件中setup_IDBFS创建的文件夹
savetodisk是从虚拟文件路径中,读取demo.xls文件,然后在浏览器端下载,这个demo.xls是在C++文件中saveexcel函数创建的
编写HTML
这就简单的多,代码就不贴了,主要就是把你刚才生成的app.js引入就行了,wasm不用引入,app.js会加载它
<script type="text/javascript" src="app.js" async></script>
运行程序
wasm文件不能直接打开运行,所以需要一个服务器方式运行,我采用的是anywhere,用其它的iis,apache都可以
运行界面如下
打开调试工具,网络->wasm,可以看到app.wasm已经加载完了
接下来就可以打开excel文件了,点击选择,选中第一步创建好的excel文件,确定
显示的内容,就是excel文件的内容
然后随便改改单元格里的内容
点保存
提示下载新的文件,下载好后打开
就是刚才改完的内容
到此,全部功能开发完成
完整源代码和演示代码去下面的地址下载
https://github.com/ltframe/code/tree/main/wasm-excel-demo
二、WebAssembly的未来
WebAssembly在速度方面,确实比Javascript提升很多,加上有很多的底层语言编写的库,这样就使得web应用的功能提升很多,比如我们通常如果要读取excel的话,一般做法是把excel上传到服务器,然后服务器解析后,再返回前端,现在有了wasm这种方式,在前端就可以搞定,这样服务器的压力少了很多。
还有,以前浏览器不能实现的功能,通过wasm也能实现了。比如,如果你把ffmpeg用WebAssembly编译后,那么浏览器上将可以播放除了mp4以外更多的视频格式,这确实就突破了浏览器本身很多的限制
未来,会有更多WebAssembly应用出现,一些原本只能运行在桌面上的应用,会随着WebAssembly的发展,更多的出现在web应用中
边栏推荐
- Web Service简单入门示例
- ProSci LAG3抗体的化学性质和应用说明
- 解读协作型机器人的日常应用功能
- Duchefa丨低熔点琼脂糖 PPC中英文说明书
- Applet project structure
- Propping of resources
- 教你自己训练的pytorch模型转caffe(一)
- The development of research tourism practical education helps the development of cultural tourism industry
- 挖财商学院给的证券账户安全吗?可以开户吗?
- Abnova CD81 monoclonal antibody related parameters and Applications
猜你喜欢
Chemical properties and application instructions of prosci Lag3 antibody
Applet page navigation
实现浏览页面时校验用户是否已经完成登录的功能
[quick start of Digital IC Verification] 2. Through an example of SOC project, understand the architecture of SOC and explore the design process of digital system
Duchefa丨P1001植物琼脂中英文说明书
Abnova total RNA Purification Kit for cultured cells Chinese and English instructions
PHP反序列化+MD5碰撞
研學旅遊實踐教育的開展助力文旅產業發展
MySQL fully parses json/ arrays
Promouvoir le développement de l'industrie culturelle et touristique par la recherche, l'apprentissage et l'enseignement pratique du tourisme
随机推荐
Nprogress plug-in progress bar
序列联配Sequence Alignment
ts 之 类的简介、构造函数和它的this、继承、抽象类、接口
XML建模
基于AVFoundation实现视频录制的两种方式
重上吹麻滩——段芝堂创始人翟立冬游记
Abbkine trakine F-actin Staining Kit (green fluorescence) scheme
bazel是否有学习的必要
国外LEAD美国简称对照表
启牛2980有没有用?开户安全吗、
3.3 project evaluation
概率论机器学习的先验知识(上)
Common view container class components
Prosci LAG-3 recombinant protein specification
Applet project structure
ClickHouse 复制粘贴多行sql语句报错
Implementation of redis unique ID generator
Abnova blood total nucleic acid purification kit pre installed relevant instructions
CCPC 2021 Weihai - G. shinyruo and KFC (combination number, tips)
Make Jar, Not War