【Rust在WASM中实现pdf文件的生成】
Rust在WASM中实现pdf文件的生成
- 前言
- 概念和依赖
- 问题描述
- 分步实现
- pdf转Blob生成URL两种方式
- 利用localstorage传递参数
- 处理图片Vec<u8>到pdf格式的Vec<u8>
- 使用rust创建iframe显示pdf的Blob
- 最后
前言
实现了一个通用的前端jpg转pdf的wasm,因为动态响应框架无法直接打印,格式会变,结合domtoimge,可以实现任意元素转jpg然后,在本地转pdf.
已上传源码,含dist,可直接使用,也可修改源码,变成自己的样子.
dist,放到任何位置, 要用时存储localstorage,并跳转过来就好了., 根据图片高宽比例,自动全屏,自动页面横竖调节…
下载地址:
https://download.csdn.net/download/wjcroom/90078261
https://github.com/wjcroom/hi/blob/master/jpg2pdf_wasm_src.tar.gz
概念和依赖
. WASM
WebAssembly(简称WASM)是一个虚拟指令集体系架构(virtual ISA),旨在为C/C++等语言编写的程序提供一种高效的二进制格式,使其能够在Web平台上以接近原生应用的运行速度运行。
跨平台:WebAssembly兼容所有主流浏览器,如Chrome、Firefox、Safari和Edge。Rust编写的代码可以轻松移植到不同的平台。
. Rust
Rust是一种系统编程语言,以其内存安全和高性能著称,是开发WebAssembly应用的理想选择
. Trunk
vesion 0,21,4
rustup 1.21.7
cargo 1.81
Trunk 是一款专为 Rust 语言设计的 WASM 网页应用打包工具。它能够帮助开发者轻松构建、打包并发布 Rust 编写的 WASM 应用到 Web 平台。Trunk 的设计理念是简单、高效,通过一个源 HTML 文件,Trunk 可以自动处理 WASM、JS 片段以及其他资源(如图片、CSS、SCSS)的打包工作。
本文的大部分依照这个而来:https://trunkrs.dev/guide/getting-started/index.html
项目:https://github.com/trunk-rs/trunk/tree/main
. printpdf
目前使用0.7的版本,
png入文档有色彩空间的问题,转为jpg格式.
https://docs.rs/printpdf/latest/printpdf/index.html
这是一个rust实现的生成pdf的工具,目前documnet-info.rs会在wasm中有两处错误。删除出错行就能在wasm环境使用。
问题描述
https://blog.csdn.net/wjcroom/article/details/143548767
在这个文章中,虽然使用了后端flask生成pdf,但唯一目的是打印,后端完全无必要来回传数据。本文要实现的wasm将图片在本地转pdf并显示。
因为这是一个验证性的工作,不具备很大的实际意义。但此架构可以构造一些功能强大本地wasn应用,比如,批量格式化一批数据到pdf格式,套用模板打印等等。
分步实现
- 图片到pdf的转换
先建立空项目
$ cargo new hello_cargo
$ cd hello_cargo
本文所用printpdf,要想开始image功能需要在Cargo.toml文件定义以下内容
[dependencies]
printpdf = { version = "0.7.0", features = [ "embedded_images" ] }
然后在main中测试主逻辑,拷贝printpdf的演示代码
fn main() {println!("Hello, world!");let (doc, page1, layer1) = PdfDocument::new("PDF_Document_title", Mm(210.0),Mm(297.0), "Layer 1");
let current_layer = doc.get_page(page1).get_layer(layer1);// currently, the only reliable file formats are bmp/jpeg/png
// this is an issue of the image library, not a fault of printpdf
let mut image_file = File::open("tmp.png").unwrap();
let image = Image::try_from( image_crate::codecs::png::PngDecoder::new(&mut image_file).unwrap()).unwrap();
let rotation_center_x = Px((image.image.width.0 as f32 / 2.0) as usize);
let rotation_center_y = Px((image.image.height.0 as f32 / 2.0) as usize);// layer,
image.add_to_layer(current_layer.clone(),ImageTransform {rotate: Some(ImageRotation {angle_ccw_degrees: 0.0,rotation_center_x,rotation_center_y,}),scale_x: Some( 2.4),scale_y: Some( 2.4),translate_x: Some(Mm(9.0)),translate_y: Some(Mm(9.0)),..Default::default()},
);doc.save(&mut BufWriter::new(File::create("test_working.pdf").unwrap())).unwrap();
}
最终会生成来自png的一个pdf,
scale_x: Some( 2.4),
scale_y: Some( 2.4), 这两个参数是x,y的放大倍数。以适应页面。translate_x这是距离左下角的,位置。
经过验证,png在0.7下有问题.改为jpg
- 建立trunk项目
rustup target add wasm32-unknown-unknown
cargo install --locked trunk
cargo new trunk-hello-world
cd trunk-hello-world
从https://github.com/trunk-rs/trunk/tree/main 的example目录,拷贝一个示例的内容,集成了printpdf是这样,
大家可以删除pdf的部分,只体会一下trunk的懒汉能量。
use printpdf::*;
use std::fs::File;
use std::io::BufWriter;
use web_sys::window;
fn start_app( show:&str) {let document = window().and_then(|win| win.document()).expect("Could not access document");let body = document.body().expect("Could not access document.body");let text_node = document.create_text_node( &format!("Hello, world from Vanilla Rust!{}",show) );body.append_child(text_node.as_ref()).expect("Failed to append text");}fn main() {console_error_panic_hook::set_once();let (doc, page1, layer1) = PdfDocument::new("PDF_Document_title", Mm(247.0), Mm(210.0), "Layer 1");
let (page2, layer1) = doc.add_page(Mm(10.0), Mm(250.0),"Page 2, Layer 1");
let pdf_bytes = doc.save_to_bytes().unwrap();// doc.save(&mut BufWriter::new(File::create("/tmp/test_working.pdf").unwrap())).unwrap(); start_app(&String::from_utf8_lossy(&pdf_bytes)) ;
}
这部分代码会显示生成空白的pdf字节的utf-8编码形式,不能显示的略过.
String::from_utf8_lossy(&pdf_bytes)。
pdf转Blob生成URL两种方式
Blob做为一个对二进制的包装,可以保存pdf的二进制数据,的rust的printpdf中是vec,呈现Blob:url的两个办法,
- 从rust-wasm函数传出vec,用js呈现,创建blob和url.在trunk项目的main.rs加入如下定义
#[wasm_bindgen]
pub fn png2pdf(pnginput: &[u8]) -Vec<u8>> {------
let pdf_bytes = doc.save_to_bytes().unwrap();
rturn pdf_bytes
}
然后的js代码中,当 加载和启动wasm模块已经结束.就可以如同js函数一样调用.
const pdfbuf=wasmBindings.png2pdf(bufferArray);const blob = new Blob([pdfbuf], { type: 'application/pdf' }); // 创建一个URL指向Blob对象,必须用数组const blobUrl = URL.createObjectURL(blob);// 打开新的标签页并导航到PDF文件的URLwindow.open(blobUrl,"_self");
其中 const blob = new Blob([pdfbuf], …必须用数组包裹pdfbuf.
接下来的方法,因为没有用数组,导致Blob无法解析成pdf.受这个js Blob的启发,在rust的Blob参数加入Vec.
2… 在rust中使用web_sys,调用window,document等实现显示pdf的blob.主要涉及插入iframe,并生成pdf,blob的URL对象
首先的Cargo.toml定义一些引用.
[dependencies]
console_error_panic_hook = "0.1.7"
wasm-bindgen = "0.2.96"
//js-sys = "0.3.10"
printpdf = { version = "0.7.0", features = [ "embedded_images" ] }
web-sys = { version = "0.3.73", features = ["Window", "Document", "HtmlElement","Blob", "Url", "BlobPropertyBag","Element","Text"] }
定义显示pdf的函数
use printpdf::*;
use std::fs::File;
use std::io::BufWriter;
use web_sys::window;
use web_sys::Element;
use web_sys::Blob;
use web_sys::Url;
use web_sys::BlobPropertyBag;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsValue;fn start_app( show:Vec<u8>) {let document = window().and_then(|win| win.document()).expect("Could not access document");let body = document.body().expect("Could not access document.body");let text_node = document.create_text_node( &format!("Hello, world from Vanilla Rust!{}","show") );let theiframe :Element = document.create_element_ns(Some(""),"div").unwrap();let jsv=JsValue::from(show);let s:Vec<JsValue>=vec![jsv]; //注意必须包装成数组.JsValue是数组类型.is_array()let jsv1=JsValue::from(s);let tye=BlobPropertyBag::new();tye.set_type("application/pdf");let blob=Blob::new_with_u8_array_sequence_and_options(&jsv1,&tye).unwrap();let curl=Url::create_object_url_with_blob(&blob).unwrap(); theiframe.set_inner_html(&format!("<iframe src='{}' width='1100px' height='600px'></iframe>",curl ));// let ifm= theiframe.dyn_into::<HtmlIFrameElement>().unwrap().clone();//ifm.set_src="d";body.append_child(text_node.as_ref()).expect("Failed to append text");body.append_child(theiframe.as_ref()).expect("Failed to append text");}
fn main() {console_error_panic_hook::set_once();let (doc, page1, layer1) = PdfDocument::new("PDF_Document_title", Mm(247.0), Mm(210.0), "Layer 1");
let (page2, layer1) = doc.add_page(Mm(20.0), Mm(250.0),"Page 2, Layer 1");
let pdf_bytes = doc.save_to_bytes().unwrap();// doc.save(&mut BufWriter::new(File::create("/tmp/test_working.pdf").unwrap())).unwrap(); start_app(pdf_bytes) ;}
这两种方法就是我找到的展现pdf的方式,其中也可接受png的字节数据.
剩下的就是集成了.
利用localstorage传递参数
放入png图片的dataurl,把这段代码放在trunk目录,index.html的开头,就能在调试过程中,指定一个图片,并且把base64的url放入localstorage.用于后期wasm,功能的调试.
<input type="file" id="imageInput" accept="image/png" /><br><img id ="smp" src="" style="bgcolor='#ffeedd'"></img>
<script>
document.getElementById('imageInput').addEventListener('change', function(e) {// 获取文件引用const file = e.target.files[0];if (!file) {return;}// 创建FileReader来读取文件const reader = new FileReader();reader.onload = function(event) {// 当文件读取完成后,event.target.result就是DataURLconst dataURL = event.target.result;// 将DataURL存储到localStoragelocalStorage.setItem('imageDataURL', dataURL);document.getElementById("smp").src= dataURL// 可以在这里添加代码来使用存储的DataURL,例如显示图片等};// 以DataURL的形式读取文件reader.readAsDataURL(file);
});
</script>
利用rust取出放入的值,先要在Cargo.toml中启用 web_sys的"Storage",feature.
然后获取 get_item()
let winw= window().unwrap();let iStorage= winw.local_storage().unwrap().unwrap();let geti=iStorage.get_item("imageDataURL").unwrap().unwrap() ;let v= data_url_to_bytes(&geti).unwrap();
转换为vec的data_url_to_bytes函数如下
use data_url::{DataUrl};fn data_url_to_bytes(data_url: &str) -> Result<Vec<u8>, &'static str> {// 尝试从提供的字符串创建一个DataUrl实例let data_url = DataUrl::process(data_url).map_err(|_| "Invalid DataURL format")?;// 确认数据部分的MIME类型是否为PNG图片if data_url.mime_type().type_!= "image" {return Err("Not a PNG image");}let (body, fragment) = data_url.decode_to_vec().unwrap();// 返回Base64解码后的数据Ok( body) }//百度AI给了个大概,一些错误排除了一个.使用data-url = "0.3.1"这个模块
处理图片Vec到pdf格式的Vec
最后的最后,将图片的vec 读入buffreader,并传递给pngdecoder,生成要插入的img,并且对img高和宽,取一个最小的放大倍数.把pdf,结果存成bytes
let (doc, page1, layer1) = PdfDocument::new("PDF_Document_title", Mm(210.0),Mm(297.0), "Layer 1");let current_layer = doc.get_page(page1).get_layer(layer1);let reader = BufReader::new(&v[..]); //v就就上面转换来的png
let img2 = Image::try_from( image_crate::codecs::png::PngDecoder::new(reader).unwrap()).unwrap();
//let image=RawImage::decode_from_bytes(v).unwrap(); let wid= Px((img2.image.width.0 as f32 / 2.0) as usize);//以一张A4大小的纸张为例,满页打印范围约为20×27.5cm ,以300dpi的解析度打印的话,换算成像素约为2362×3248, let scalex:f32= 2302.0 as f32/img2.image.width.0 as f32;let scaley:f32= 3248.0 as f32/img2.image.height.0 as f32;let scale = f32::min(scaley,scalex);alert(&format!("{}",scale));img2.add_to_layer(current_layer.clone(), ImageTransform {scale_x: Some( scale),scale_y: Some( scale ),translate_x: Some(Mm(8.0)),translate_y: Some(Mm(8.0)),..Default::default()},
);let pdf_bytes = doc.save_to_bytes().unwrap();
使用rust创建iframe显示pdf的Blob
let jsv=JsValue::from(pdf_bytes);let s:Vec<JsValue>=vec![jsv];let jsv1=JsValue::from(s);let tye=BlobPropertyBag::new();tye.set_type("application/pdf");let blob=Blob::new_with_u8_array_sequence_and_options(&jsv1,&tye).unwrap();
//let blob=Blob::new_with_u8_array_sequence(&jsv).unwrap();let curl=Url::create_object_url_with_blob(&blob).unwrap(); let theiframe :Element = document.create_element_ns(Some(""),"div").unwrap();theiframe.set_inner_html(&format!("<iframe src='{}' width='1100px' height='600px'></iframe>",curl ));// let ifm= theiframe.dyn_into::<HtmlIFrameElement>().unwrap().clone();//ifm.set_src="d";body.append_child(text_node.as_ref()).expect("Failed to append text");body.append_child(theiframe.as_ref()).expect("Failed to append text");
好至此全部内容结束.
最后
pdf的blob显示在一个iframe 中 的js代码:
<!DOCTYPE html>
<html>
<head><title>PDF Viewer</title>
</head>
<body><iframe id="pdfViewer" width="100%" height="800px"></iframe><script>// 假设这是你已经有的PDF Blob数据var pdfBlob = yourPDFBlobData; // 这里应该是你实际的Blob数据// 创建一个新的URL,指向这个Blobvar url = URL.createObjectURL(pdfBlob);// 获取iframe元素并设置其src属性为PDF的URLvar viewer = document.getElementById('pdfViewer');viewer.src = url;</script>
</body>
</html>
trunk sever是边改,边自动编译的命令,但是有个别时候,需要手工中止,ctr +c、然后再启动一次。
运行无错,均已实现后可truck build,生成的内容,一个*.wasm , 一个js,一个html、
参考html加入两个库到现有的页面,即可实现本文的要求了。
细节请根据文档调整。
由于时间仓促,且实际已无大的阻碍,当前没有实现最终版。所以本文暂时如此,
12/1后期看情形,将更新为上线运行版的代码。
12/3在一天以后,已经做了最后的更新,目前代码可以,从localstorage获取png的base64. rust获取处理成bytes后,用pdf,加载,然后在页面插入iframe,并显示此pdf.并且定义了,高和宽的自动缩放,然后没有启动压缩,据说发布版本可以压缩,从5M,下降到几百kb.
好了,我先realse一下看看.
12/4几天前我也不知怎么上线运行,既然要搞得有个结果,何况自己的会议签到还等着出替代方案.改服务端为本地转换,4号修复了png显示不全的问题,改为jpg.因为实在想出现一个能用的wasm,pdf生成的东西.所以最后调整了,自动横排,自动扩展.就匆忙上线了.本来放入pan.ezdial.cn.怎耐免费透传总是停机换参数. 比较气人.索性源码和二进制dist放入csdn下载.
效果
结束了.
特别感谢 【前端柒八九】提供的 Rust赋能前端:纯血前端将table导出excel提供的启发。
相关文章:

【Rust在WASM中实现pdf文件的生成】
Rust在WASM中实现pdf文件的生成 前言概念和依赖问题描述分步实现pdf转Blob生成URL两种方式利用localstorage传递参数处理图片Vec<u8>到pdf格式的Vec<u8>使用rust创建iframe显示pdf的Blob最后 前言 实现了一个通用的前端jpg转pdf的wasm,因为动态响应框架无法直接打…...

在MySQL中执行sum case when报错:SUM does not exist
1. 报错 在pgsql中能正常运行的一段SQL在MySQL中运行的时候报错了: SELECT DATE( hr.handle_time ) AS statsDate,SUM ( CASE WHEN hma.app_type IN ( 2, 5 ) THEN ch_money ELSE 0 END ) AS aliPayAmt,SUM ( CASE WHEN hma.app_type IN ( 1, 4 ) THEN ch_money EL…...

【openssl】相关指令
熟悉下相关概念 x509:证书标准pem和der:两种(包括公私钥、证书签名请求、证书等内容的)的格式,前者是文本形式,linux常用,后者是二进制形式,windows常用,仅仅是格式&…...

实例分割详解
实例分割详解 引言 实例分割是计算机视觉领域的一项复杂任务,它要求模型能够识别图像中不同类别的对象,并对每个单独的对象进行像素级别的分类。与语义分割不同的是,实例分割不仅要区分不同的类别,还要识别同一类别中的不同个体…...

D87【python 接口自动化学习】- pytest基础用法
day87 pytest运行参数 -m -k 学习日期:20241203 学习目标:pytest基础用法 -- pytest运行参数-m -k 学习笔记: 常用运行参数 pytest运行参数-m -k pytest -m 执行特定的测试用例,markers最好使用英文 [pytest] testpaths./te…...

浅谈MySQL路由
华子目录 mysql-router介绍下载mysql-router安装mysql-router实验 mysql-router介绍 mysql-router是一个对应用程序透明的InnoDB Cluster连接路由服务,提供负载均衡、应用连接故障转移和客户端路由利用路由器的连接路由特性,用户可以编写应用程序来连接到…...

matlab中disp,fprintf,sprintf,display,dlmwrite输出函数之间的区别
下面是他们之间的区别: disp函数与fprintf函数的区别 输出格式的灵活性 disp函数:输出格式相对固定。它会自动将变量以一种比较直接的方式显示出来。对于数组,会按照行列形式展示;对于字符串,直接原样输出并换行。例如…...

30.100ASK_T113-PRO 用QT编写视频播放器(一)
1.再buildroot中添加视频解码库 X264, 执行 make menuconfig Target packages -->Libraries --> Multimedia --> X264 CLI 还需要添加 FFmpeg 2. 保存,重新编译 make all 3.将镜像下载开发板...

Linux-GPIO应用编程
本章介绍应用层如何控制 GPIO,譬如控制 GPIO 输出高电平、或输出低电平。 只要是用到GPIO的外设,都有可能用得到这些操作方法。 照理说,GPIO的操作应该是由驱动层去做的,使用寄存器操作或者GPIO子系统之类的框架。 但是࿰…...

opencvocr识别手机摄像头拍摄的指定区域文字,文字符合规则就语音报警
安装python,pycharm,自行安装。 Python下安装OpenCv 2.1 打开cmd,先安装opencv-python pip install opencv-python --user -i https://pypi.tuna.tsinghua.edu.cn/simple2.2 再安装opencv-contrib-python pip install opencv-contrib-python --user …...

微服务即时通讯系统(5)用户管理子服务,网关子服务
用户管理子服务(user文件) 用户管理子服务也是这个项目中的一个业务最多的子服务,接口多,但是主要涉及的数据表只有user表,Redis的键值对和ES的一个搜索引擎,主要功能是对用户的个人信息进行修改管理&#…...

postgreSQL安装后启动有The application server could not be contacted问题
不得不说pgsql是真的麻烦,找问题找了几个小时才解决.直接步入主题吧 首先问题如下 安装后,双击启动就出现上述问题 首先删除路径为 c:\Users\your_name\AppData\Roaming\pgAdmin 之内的所有文件和文件夹, 如果找不到AppData,就把这个点开 接着找到你安装pgsql的路径,我的是D…...

架构05-架构安全性
零、文章目录 架构05-架构安全性 1、软件架构安全的重要性 **系统安全:**不仅包括防御黑客攻击,还包括安全备份与恢复、安全审计、防治病毒等。**关注重点:**认证、授权、凭证、保密、传输安全、验证。 2、认证(Authenticatio…...

虚幻引擎---材质篇
一、基础知识 虚幻引擎中的材质(Materials) 定义了场景中对象的表面属性,包括颜色、金属度、粗糙度、透明度等等;可以在材质编辑器中可视化地创建和编辑材质;虚幻引擎的渲染管线的着色器是用高级着色语言(…...

NPM镜像详解
NPM镜像详解 什么是NPM镜像 NPM镜像(NPM Mirror)是一个完整的NPM包的副本服务器。由于npm的官方registry服务器部署在国外,国内访问可能会比较慢,因此使用镜像可以加快包的下载速度。 常用的NPM镜像源 npm官方镜像 https://reg…...

从智能合约到去中心化AI:Web3的技术蓝图
Web3正在成为互联网发展的重要方向,其核心理念是去中心化、用户主权和自治。随着区块链技术、智能合约以及人工智能(AI)等技术的发展,Web3不仅重新定义了数据存储和交易方式,还为更智能化、去中心化的数字生态系统铺平…...

STM32进阶 定时器3 通用定时器 案例1:LED呼吸灯——PWM脉冲
功能 它有基本定时器所有功能,还增加以下功能 TIM2、TIM3、TIM4、TIM5 多种时钟源: 外部时钟源模式1: 每个定时器有四个输入通道 只有通道1和通道2的信号可以作为时钟信号源 通道1 和通道2 的信号经过输入滤液和边缘检测器 外部时钟源…...

开源即时通讯与闭源即时通讯该怎么选择,其优势是什么?
在选择即时通讯软件时,应根据企业的经营领域来选择适合自身需求的开源或闭源方案。不同领域对开源和闭源即时通讯的理念存在差异,因此总结两个点简要分析这两种选择,有助于做出更明智的决策。 一、开源与闭源的根本区别在于软件的源代码是否…...

930[water]
算法...

2024论文翻译 | Multi-Review Fusion-in-Context
摘要 接地气的文本生成,包括长篇问答和摘要等任务,需要同时进行内容选择和内容整合。当前的端到端方法由于其不透明性,难以控制和解释。因此,近期的研究提出了一个模块化方法,每个步骤都有独立的组件。具体来说&#…...

(78)MPSK基带调制通信系统瑞利平坦衰落信道传输性能的MATLAB仿真
文章目录 前言一、MATLAB仿真1.仿真代码2.仿真结果 二、子函数与完整代码总结 前言 本文给出瑞利平坦衰落信道上的M-PSK通信系统性能仿真的MATLAB源代码与仿真结果。其中,调制方式M-PSK包括BPSK、QPSK、8-PSK、16-PSK、32-PSK等方式。 一、MATLAB仿真 1.仿真代码 …...
【机器学习】机器学习的基本分类-监督学习-决策树-CART(Classification and Regression Tree)
CART(Classification and Regression Tree) CART(分类与回归树)是一种用于分类和回归任务的决策树算法,提出者为 Breiman 等人。它的核心思想是通过二分法递归地将数据集划分为子集,从而构建一棵树。CART …...

【金猿CIO展】复旦大学附属中山医院计算机网络中心副主任张俊钦:推进数据安全风险评估,防范化解数据安全风险,筑牢医疗数据安全防线...
张俊钦 本文由复旦大学附属中山医院计算机网络中心副主任张俊钦撰写并投递参与“数据猿年度金猿策划活动——2024大数据产业年度优秀CIO榜单及奖项”评选。 大数据产业创新服务媒体 ——聚焦数据 改变商业 数据要素时代,医疗数据已成为医院运营与决策的重要基石…...

工业机器视觉-基于深度学习的水表表盘读数识别
字轮数字识别、指针读数识别(角度换算)、根据指针角度进行读数修正、根据最高位指针(x0.1)读数对字轮数字进行修正、得到最终读数。 基于深度学习的目标检测技术和OpenCV图像处理技术,可识别所有类型的表盘机械读数。...

基于ZooKeeper搭建Hadoop高可用集群
ZooKeeper搭建Hadoop高可用集群 在之前安装的Hadoop3.3.6集群中HDFS NameNode 和 YARN ResourceManager 都是单节点,集群不具有高可用性。 HDFS 高可用架构 HDFS 高可用架构主要组件: Active NameNode 和 Standby NameNode: 两台 NameNode…...

力扣88题:合并两个有序数组
力扣88题:合并两个有序数组 题目描述 给定两个按非递减顺序排列的整数数组 nums1 和 nums2,以及它们的长度 m 和 n,要求将 nums2 合并到 nums1,使得合并后的数组仍按非递减顺序排列。 输入与输出 示例 1: 输入&am…...

python 笔记之线程同步和死锁
同步: 共享数据: 如果多个线程共同对某个数据修改,则可能出现不可预测的结果,为了保证数据的正确性,需要对多个数据进行同步 同步:一个一个的完成,一个做完另一个才能进来 效率会降低 使用Thre…...

SpringBoot小知识(4):高级配置知识与bean的绑定
一、EnableConfigurationProperties ConfigurationProperties注解在我们之前讲过,他是从配置中读取参数封装给实体类的一个注解。 那么EnableConfigurationProperties是个啥呢? EnableConfigurationProperties 是 Spring Framework 中用于启用基于配置文…...

Python毕业设计选题:基于大数据的淘宝电子产品数据分析的设计与实现-django+spark+spider
开发语言:Python框架:djangoPython版本:python3.7.7数据库:mysql 5.7数据库工具:Navicat11开发软件:PyCharm 系统展示 管理员登录 管理员功能界面 电子产品管理 系统管理 数据可视化分析看板展示 摘要 本…...

Lua面向对象实现
Lua中的面向对象是通过表(table)来模拟类实现的,通过setmetatable(table,metatable)方法,将一个表设置为当前表的元表,之后在调用当前表没有的方法或者键时,会再查询元表中的方法和键,以此来实现…...