Tauri2+Leptos开发桌面应用--Sqlite数据库操作

 在之前工作(使用Tauri + Leptos开发带系统托盘桌面应用-CSDN博客)的基础上,继续尝试对本地Sqlite数据库进行读、写、删除操作,开发环境还是VS Code+Rust-analyzer。

最终程序界面如下:

 主要参考文章:Building a todo app in Tauri with SQLite and sqlx

1. 创建项目

还是用create-tauri-app来创建一个新项目,并安装sqlx-cli。

cargo create-tauri-app
cargo install sqlx-cli

create-tauri-app升级到了4.5.9版本,Leptos也从0.6版本更新到了0.7版本。

src-tauri/Cargo.toml文件内容如下: 

[package]
name = "acid-index"
version = "0.1.0"
description = "A Tauri App"
authors = ["you"]
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[lib]
# The `_lib` suffix may seem redundant but it is necessary
# to make the lib name unique and wouldn't conflict with the bin name.
# This seems to be only an issue on Windows, see https://github.com/rust-lang/cargo/issues/8519
name = "acid_index_lib"
crate-type = ["staticlib", "cdylib", "rlib"]

[build-dependencies]
tauri-build = { version = "2", features = [] }

[dependencies]
tauri = { version = "2", features = ["tray-icon"] }
tauri-plugin-opener = "2"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
#tauri-plugin-sql = {version = "2", features = ["sqlite"] }  # or "postgres", or "mysql"
sqlx = { version = "0.8.2", features = ["sqlite", "runtime-tokio"] }
tokio = { version ="1", features = ["full"] }
futures = "0.3.31"
log = "0.4.22"

#for win7
#tauri-plugin-notification = { version = "2", features = [ "windows7-compat" ] }

VS Code的rust-analyzer插件会自动完成相关依赖包的安装

2. Sqlite数据库操作

 使用sqlx实现对Sqlite数据库的操作,数据库文件名为:db.sqlite,默认在appDataDir文件夹下面,这个找了很久,Win10系统下为:C:\Users\<user_name>\AppData\Roaming\<identifier>\ 文件夹,其中"username"为用户名,"identifier"为tauri.conf.json中定义的"identifier",此处为com.acid-index.app。

本例中新建一个数据库,包含一个users表格。数据库迁移文件在src-tauri/migrates/文件夹下面,使用如下命令生成:

cd src-tauri
sqlx migrate add create_users_table

然后修改 src-tauri/migrates/文件夹下面的sql文件,内容如下:

-- Add migration script here
CREATE TABLE IF NOT EXISTS users (
                id INTEGER PRIMARY KEY AUTOINCREMENT,  
                username TEXT NOT NULL,  
                email TEXT
        );

还是通过#[tauri::command]新建invoke handle函数实现对数据库的操作,用于前端leptos调用。主要程序在src-tauri\src\lib.rs中,具体如下:

use futures::TryStreamExt;
use sqlx::{migrate::MigrateDatabase, prelude::FromRow, sqlite::SqlitePoolOptions, Pool, Sqlite};
use tauri::{App, Manager};

type Db = Pool<Sqlite>;
struct DbState {
    db: Db,
}

async fn setup_db(app: &App) -> Db {
    let mut path = app.path().app_data_dir().expect("获取程序数据文件夹路径失败!");
 
    match std::fs::create_dir_all(path.clone()) {
        Ok(_) => {}
        Err(err) => {
            panic!("创建文件夹错误:{}", err);
        }
    };

    //C:\Users\<user_name>\AppData\Roaming\com.mynewapp.app\db.sqlite 
    path.push("db.sqlite");
 
    Sqlite::create_database(
        format!("sqlite:{}", path.to_str().expect("文件夹路径不能为空!")).as_str(),
        )
        .await
        .expect("创建数据库失败!");
 
    let db = SqlitePoolOptions::new()
        .connect(path.to_str().unwrap())
        .await
        .unwrap();
    
    //创建迁移文件位于./migrations/文件夹下    
    //cd src-tauri
    //sqlx migrate add create_users_table
    sqlx::migrate!("./migrations/").run(&db).await.unwrap();
 
    db
}


// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
#[tauri::command]
fn greet(name: &str) -> String {
    format!("你好, {}!Rust向你问候了!", name)
}

use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, FromRow)]
struct User {
    id: u16,
    username: String,
    email: String,
}

#[derive(Debug, Serialize, Deserialize, FromRow)]
struct UserId {
    id: u16,
}

#[tauri::command]
async fn get_db_value(state: tauri::State<'_, DbState>, window: tauri::Window) -> Result<String, String> {
    let db = &state.db;
    let query_result:Vec<User> = sqlx::query_as::<_, User>(       //查询数据以特定的格式输出
        "SELECT * FROM users"
        )
        .fetch(db)
        .try_collect()
        .await.unwrap();
    
    let mut div_content = String::new();
    for user in query_result.iter(){
        div_content += &format!(r#"<p>ID:{},姓名:{},邮箱:{}</p>"#, user.id, user.username, user.email);
    }   
    // 获取当前窗口
    let current_window = window.get_webview_window("main").unwrap();
    let script = &format!("document.getElementById('db-item').innerHTML = '{}';",div_content);
    current_window.eval(script).unwrap();

    Ok(String::from("数据库读取成功!"))
}


//use tauri::Error;
#[tauri::command]
async fn insert_db_item(state: tauri::State<'_, DbState>, username: &str, email: Option<&str>) -> Result<String, String> {
    let db = &state.db;
    email.unwrap_or("not set yet");     //email类型为Option<&str>,其结果为None或者Some(&str)

    sqlx::query("INSERT INTO users (username, email) VALUES (?1, ?2)")
        .bind(username)
        .bind(email)
        .execute(db)
        .await
        .map_err(|e| format!("数据库插入项目错误: {}", e))?;
    
    Ok(String::from("插入数据成功!"))
}


#[tauri::command]
async fn del_last_user(state: tauri::State<'_, DbState>) -> Result<String, String> {
    let db = &state.db;

    let last_id:UserId = sqlx::query_as::<_,UserId>("SELECT id FROM users ORDER BY id DESC LIMIT 1")
        .fetch_one(db)
        .await
        .unwrap();

        sqlx::query("DELETE FROM users WHERE id = ?1")
        .bind(last_id.id)
        .execute(db)
        .await
        .map_err(|e| format!("could not delete last user: {}", e))?;

        Ok(String::from("最后一条数据删除成功!"))
}


mod tray;       //导入tray.rs模块


#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        .plugin(tauri_plugin_opener::init())
        .invoke_handler(tauri::generate_handler![greet, get_db_value, insert_db_item, update_user, del_last_user])
        .setup(|app| {
            #[cfg(all(desktop))]
            {
                let handle = app.handle();
                tray::create_tray(handle)?;         //设置app系统托盘
            }
            tauri::async_runtime::block_on(async move {
                let db = setup_db(&app).await;         //setup_db(&app:&mut App)返回读写的数据库对象
                app.manage(DbState { db });                   //通过app.manage(DbState{db})把数据库对象传递给state:tauri::State<'_, DbState>
            });
            Ok(())
            })
        .run(tauri::generate_context!())
        .expect("运行Tauri程序的时候出错!");
}

setup_db函数新建了数据库,设置迁移(如数据库文件存在将直接使用),并将数据库返回给了全局状态:tauri::Builder::default().manager(state),这样注册的invoke handle函数就可以直接调用数据库。

对Leptos不熟悉,get_db_value函数对前端界面元素的操作也是在src-tauri\src\lib.rs中通过调用javascript脚本实现的。其实更好的办法是将数据库查询的结果通过invoke传递给前端,前端Leptos再对接收到的JsValue数据进行处理。

具体修改get_db_value函数如下:

#[tauri::command]
async fn get_db_value(state: tauri::State<'_, DbState>) -> Result<Vec<User>, String> {
    let db = &state.db;
    let query_result:Vec<User> = sqlx::query_as::<_, User>(       //查询到的数据以特定的格式输出
        "SELECT * FROM users"
        )
        .fetch(db)
        .try_collect()
        .await.unwrap();
    Ok(query_result)        //查询结果传递给前端
}

前端需要定义接收数据的格式,然后使用serde_wasm_bindgen::from_value()将invoke得到的JsValue数据格式转换成rust数据格式以供使用(因为前端是Leptos)。

#[derive(Serialize, Deserialize)]
struct User {
    id: u16,
    username: String,
    email: String,
}


#[component]
pub fn App(initial_value:i32) -> impl IntoView {         

......

    let get_db_item = move |ev: SubmitEvent| {
        ev.prevent_default();           //类似javascript中的Event.preventDefault()
        spawn_local(async move {
            let js_vec = invoke_without_args("send_db_item").await;    //调用得到的数据类型为JsValue
            //使用serde_wasm_bindgen::from_value()将JsValue数据格式转换成rust数据格式
            let rust_vec: Vec<User> = serde_wasm_bindgen::from_value(js_vec).map_err(|_| JsValue::from("Deserialization error")).unwrap();
            let mut receive_msg = String::from("读取数据库ID序列为:[");
            for user in rust_vec{
                receive_msg += &format!("{}, ", user.id);
            }
            receive_msg += "]";

/*在此处理invoke过来的数据*/

            set_insert_msg.set(receive_msg);
        });
    };
}

 3. 前端Leptos调用

主要是界面元素设计(包括style.css)和Invoke调用,对于没有参数的调用,要增加如下设置:

#[wasm_bindgen]
extern "C" {
+   #[wasm_bindgen(js_namespace = ["window", "__TAURI__", "core"], js_name = invoke)]
+   async fn invoke_without_args(cmd: &str) -> JsValue;

    #[wasm_bindgen(js_namespace = ["window", "__TAURI__", "core"])] //Tauri API 将会存储在 window.__TAURI__ 变量中,并通过 wasm-bindgen 导入。
    async fn invoke(cmd: &str, args: JsValue) -> JsValue;
}

src/app.rs文件内容如下:

use leptos::task::spawn_local;
use leptos::{ev::SubmitEvent, prelude::*};
use serde::{Deserialize, Serialize};        //#derive(serialize, Deserialize)用于请求响应传递参数的序列化
use wasm_bindgen::prelude::*;


//‌WASM(WebAssembly)是一种低级字节码格式,可以从C、C++、Rust等高级语言编译而来,旨在在Web端实现接近原生的执行效率‌‌
//wasm 并不是传统意义上汇编语言(Assembly),而是一种中间编译的字节码,可以在浏览器上运行非 JavaScript 编写的代码
//wasm-bindgen主要目的是实现Rust与现有JavaScript环境的无缝集成‌,自动生成必要的绑定和胶水代码,确保Rust函数和JavaScript之间平滑通信
#[wasm_bindgen]
extern "C" {
    #[wasm_bindgen(js_namespace = ["window", "__TAURI__", "core"], js_name = invoke)]
    async fn invoke_without_args(cmd: &str) -> JsValue;

    #[wasm_bindgen(js_namespace = ["window", "__TAURI__", "core"])] //Tauri API 将会存储在 window.__TAURI__ 变量中,并通过 wasm-bindgen 导入。
    async fn invoke(cmd: &str, args: JsValue) -> JsValue;
}


//derive是一个编译器指令,在类型定义(如结构体或枚举)上添加#[derive(...)]让编译器为一些特性提供基本的实现,
//...表示要为其提供基本实现的特性列表。#[derive(Serialize, Deserialize)],可以轻松地为结构体实现序列化和反序列化功能。
//要在Rust中使用序列化和反序列化,首先需要在Cargo.toml文件中引入serde库
//serde = { version = "1.0", features = ["derive"] }
//serde_json = "1.0"
//序列化后的变量作为函数invoke(cmd, args: JsValue)的参数,JsValue为序列化格式
#[derive(Serialize, Deserialize)]
struct GreetArgs<'a> {
    name: &'a str,
}

#[derive(Serialize, Deserialize)]
struct InsertArgs<'a> {      //生命周期标识符 'a 用于帮助编译器检查引用的有效性,避免悬垂引用和使用已被释放的内存。
    username: &'a str,
    email: &'a str,         
}

#[derive(Serialize, Deserialize)]
struct User {
    id: u16,
    username: String,
    email: String,
}

//Component是一个重要的概念,它通常指的是项目中的一部分代码或功能模块。
#[component]
pub fn App(initial_value:i32) -> impl IntoView {         //函数返回IntoView类型,即返回view!宏,函数名App()也是主程序view!宏中的组件名(component name)。
    let (name, set_name) = signal(String::new());
    let (greet_msg, set_greet_msg) = signal(String::new());
    let (value, set_value) = signal(initial_value);
    let (username, set_username) = signal(String::new());
    let (email, set_email) = signal(String::new());
    let (insert_msg, set_insert_msg) = signal(String::new());
    //let (fetch_msg, set_fetch_msg) = signal(String::new());

    let update_name = move |ev| {       //将闭包传递给按钮组件的onclick参数,鼠标点击触发事件,更新之前创建signal中的值。
        let v = event_target_value(&ev);
        set_name.set(v);
    };


    let get_db_item = move |ev: SubmitEvent| {
        ev.prevent_default();           //类似javascript中的Event.preventDefault(),处理<input>字段非常有用
        spawn_local(async move {                //使用Leptos的spawn_local创建一个本地线程(local_thread)Future, 提供一个异步move闭包。
            let new_msg = invoke_without_args("get_db_value").await.as_string().unwrap();
            set_insert_msg.set(new_msg);

        });
    };

    let greet = move |ev: SubmitEvent| {
        ev.prevent_default();           //类似javascript中的Event.preventDefault(),处理<input>字段非常有用
        spawn_local(async move {                //使用Leptos的spawn_local创建一个本地线程(local_thread)Future, 提供一个异步move闭包。
            let name = name.get_untracked();    //防止通常由Leptos signal产生的反应式绑定,因此即使值发生变化,Leptos也不会尝试更新闭包。
            if name.is_empty() {
                return;
            }

            let args = serde_wasm_bindgen::to_value(&GreetArgs { name: &name }).unwrap();   //参数序列化
            // Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
            let new_msg = invoke("greet", args).await.as_string().unwrap();     //使用invoke调用greet命令,greet类似于API
            set_greet_msg.set(new_msg);
        });
    };

    let update_user = move |ev| {       //将闭包传递给按钮组件的onclick参数,更新之前创建signal中的值。
        let v = event_target_value(&ev);
        set_username.set(v);
    };

    let update_email = move |ev| {       //将闭包传递给按钮组件的onclick参数,更新之前创建signal中的值。
        let v = event_target_value(&ev);
        set_email.set(v);
    };

    let insert_db_item = move|ev:SubmitEvent| {
        ev.prevent_default();
        spawn_local(async move {
            let username = username.get_untracked();
            let email = email.get_untracked();
            if username.is_empty() {
                return;
            }

            let args = serde_wasm_bindgen::to_value(&InsertArgs { username: &username, email:&email }).unwrap();   //参数序列化
            // Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
            let new_msg = invoke("insert_db_item", args).await.as_string().unwrap_or(String::from("Insert Data Failed!!!"));     //使用invoke调用insert_db_item命令,greet类似于API
            set_insert_msg.set(new_msg);
        });     

    };

    let del_last_item = move|ev:SubmitEvent| {
        ev.prevent_default();
        spawn_local(async move {                //使用Leptos的spawn_local创建一个本地线程(local_thread)Future, 提供一个异步move闭包。
            let new_msg = invoke_without_args("del_last_user").await.as_string().unwrap_or(String::from("Delete Data Failed!!!")); 
            set_insert_msg.set(new_msg);
        });
    };  

 
    let clear = move |_| {set_value.set(0);};
    let decrement = move |_| {set_value.update(|v| *v -=1);};       //update()函数接受闭包,参数是signal的setter值set_value
    let increment = move |_| {set_value.update(|v| *v +=1);};



    view! {                                              //view!宏作为App()函数的返回值返回IntoView类型
        <main class="container">
            <h1>"Welcome to Tauri(2.1.1) + Leptos(0.7.2)"</h1>

            <div class="row">
                <a href="https://tauri.app" target="_blank">
                    <img src="public/tauri.svg" class="logo tauri" alt="Tauri logo"/>
                </a>
                <a href="https://docs.rs/leptos/" target="_blank">
                    <img src="public/leptos.svg" class="logo leptos" alt="Leptos logo"/>
                </a>
                <a href="https://scybbd.com" target="_blank">
                    <img src="public/doughnutS.svg" class="logo scybbd" alt="YislWll's website"/>
                </a>
            </div>
            <p>"点击Tauri、Leptos和Scybbd的logo了解更多..."</p>

            <form class="row" id="greet-form" on:submit=greet>
                <input
                    id="greet-input"
                    placeholder="请输入一个名字..."
                    on:input=update_name
                />
                <button type="submit" id="greet-button">"打招呼"</button>
            </form>
            <p>{ move || greet_msg.get() }</p>

            <div>
                <button style="margin:0 10px 0 10px;" on:click = clear>"清零"</button>
                <button style="margin:0 10px 0 10px;" on:click = decrement>"-1"</button>
                <span style="margin:0 10px 0 10px;" class:red=move||{value.get()<0}>"数值:"{value}</span>     //当值小于0时,字体变成红色
                <button style="margin:0 10px 0 10px;" on:click = increment>"+1"</button>
            </div>
            <p></p>
            <form class="row" id="insert-form" on:submit=insert_db_item>
                <input
                    id="user-input"
                    placeholder="请输入姓名..."
                    on:input=update_user
                    style="margin:0 10px 0 10px;" 
                />
                <input
                id="email-input"
                placeholder="请输入邮箱(可选)..."
                on:input=update_email
                style="margin:0 10px 0 10px;" 
                />
                <button type="submit" id="insert-button"  style="margin:0 10px 0 10px;" >"插入数据库"</button>
            </form>
            <p class="info">数据库变动信息:{ move || insert_msg.get() }</p> 
            <div class="form-container">
                <div class="db-window" id="db-item">
                    <p></p>
                    </div>
                <div class="btn-window">
                    <form class="row" on:submit=get_db_item>
                        <button type="submit" style="margin:10px 5px 10px 5px;" id="get-button" style="margin:0 10px 0 10px;" >"读取数据库"</button>
                    </form>
                    <form class="row" on:submit=del_last_item>
                    <button type="submit" style="margin:10px 5px 10px 5px;" id="del-button" style="margin:0 10px 0 10px;" >"删除最后项"</button>
                    </form>
                </div>
            </div>
        </main>
    }
}

Leptos 0.7版与0.6版有所区别,create_signal换成了signal。

至此,Tuari+Leptos应用程序通过sqlx对数据库进行操作的简单功能基本完成。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/944286.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

设计模式之状态模式:自动售货机的喜怒哀乐

~犬&#x1f4f0;余~ “我欲贱而贵&#xff0c;愚而智&#xff0c;贫而富&#xff0c;可乎&#xff1f; 曰&#xff1a;其唯学乎” 一、状态模式概述 \quad 在我们的日常生活中&#xff0c;很多事物都具有不同的状态。比如我们经常使用的自动售货机&#xff0c;它就具有多种状态…

4.银河麒麟V10(ARM) 离线安装 MySQL

1. 系统版本 [rootga-sit-cssjgj-db-01u ~]# nkvers ############## Kylin Linux Version ################# Release: Kylin Linux Advanced Server release V10 (Lance)Kernel: 4.19.90-52.39.v2207.ky10.aarch64Build: Kylin Linux Advanced Server release V10 (SP3) /(La…

多模态论文笔记——LLaVA

大家好&#xff0c;这里是好评笔记&#xff0c;公主号&#xff1a;Goodnote&#xff0c;专栏文章私信限时Free。本文详细介绍多模态模型&#xff1a;LLaVA。处理包含图像和文本的多模态数据&#xff0c;并生成合理准确的回答。 文章目录 论文模型架构视觉编码器语言模型多模态融…

【Sentinel】初识Sentinel

目录 1.1.雪崩问题及解决方案 1.1.1.雪崩问题 1.1.2.超时处理 1.1.3.仓壁模式 1.1.4.断路器 1.1.5.限流 1.1.6.总结 1.2.服务保护技术对比 1.3.Sentinel介绍和安装 1.3.1.初识Sentinel 1.3.2.安装Sentinel 1.4.微服务整合Sentinel 1.1.雪崩问题及解决方案 1.1.1.…

[A-24][V-09]ARMv8/v9-SMMU工作场景与SMMU的虚拟化架构

ver0.1 [看前序文章有惊喜,关注W\X\G=Z+H=“浩瀚架构师”,可以解锁全部文章] 前言 我们在介绍ARM的内存体系的时候,行文中经常讲MMU比作PE-Cores的带刀护卫。按照这个逻辑,那么SMMU也可以称之为总线上各个Master(设备)的带刀护卫,利刃出鞘之后,任何驱动送过来的地址都…

WebRTC服务质量(10)- Pacer机制(02) RoundRobinPacketQueue

WebRTC服务质量&#xff08;01&#xff09;- Qos概述 WebRTC服务质量&#xff08;02&#xff09;- RTP协议 WebRTC服务质量&#xff08;03&#xff09;- RTCP协议 WebRTC服务质量&#xff08;04&#xff09;- 重传机制&#xff08;01) RTX NACK概述 WebRTC服务质量&#xff08;…

硬件设计-时钟振荡器

目录 摘要 壳式晶振 正常工作条件 摘要 本章主要介绍了晶振的分类、各项参数的意义、特点&#xff0c;同时也介绍了时钟抖动的成因、测量 方法、消除措施和典型滤波电路&#xff0c;使得我们可以正确地选择和使用晶振。 壳式晶振 如图 所示&#xff0c;壳式晶振的名字来源于…

Redis基础知识分享(含5种数据类型介绍+增删改查操作)

一、redis基本介绍 1.redis的启动 服务端启动 pythonubuntu:~$ redis-server客户端启动 pythonubuntu:~$ redis-cli <127.0.0.1:6379> exit pythonubuntu:~$ redis-cli --raw //(支持中文的启动方式) <127.0.0.1:6379> exit2.redis基本操作 ping发送给服务器…

sql字段值转字段

表alertlabel中记录变字段 如何用alertlabel表得到下面数据 实现的sql语句 select a.AlertID, (select Value from alertlabel where AlertIDa.AlertID and Labelhost) as host, (select Value from alertlabel where AlertIDa.AlertID and Labeljob) as job from (select …

llamafactory报错:双卡4090GPU,训练qwen2.5:7B、14B时报错GPU显存不足(out of memory),轻松搞定~~~

实际问题场景&#xff1a; 使用llamafactory进行微调qwen2.5 7B和14B的大模型时&#xff0c;会出现out of memory的报错。尝试使用降低batch_size&#xff08;原本是2&#xff0c;现在降到1&#xff09;的方式&#xff0c;可以让qwen2.5:7B跑起来&#xff0c;但时不时会不稳定…

【hackmyvm】hacked靶机wp

tags: HMVrootkitDiamorphine Type: wp 1. 基本信息^toc 文章目录 1. 基本信息^toc2. 信息收集2.1. 端口扫描2.2. 目录扫描2.3. 获取参数 3. 提权 靶机链接 https://hackmyvm.eu/machines/machine.php?vmHacked 作者 sml 难度 ⭐️⭐️⭐️⭐️️ 2. 信息收集 2.1. 端口扫描…

.NET平台用C#通过字节流动态操作Excel文件

在.NET开发中&#xff0c;通过字节流动态操作Excel文件提供了一种高效且灵活的方式处理数据。这种方法允许开发者直接在内存中创建、修改和保存Excel文档&#xff0c;无需依赖直接的文件储存、读取操作&#xff0c;从而提高了程序的性能和安全性。使用流技术处理Excel不仅简化了…

应用层1——C/S、P2P、DNS域名系统

目录 一、网络应用模型 1、C/S 2、p2p模型 二、域名解析系统DNS 1、为什么有DNS系统&#xff1f; 2、域名的特点 3、DNS域名系统原理 4、递归查询、迭代查询 5、常用的根域名与顶级域名 一、网络应用模型 1、C/S 客户/服务器模型 客户请求服务&#xff0c;服务器提供…

【疑难杂症】 HarmonyOS NEXT中Axios库的响应拦截器无法拦截424状态码怎么办?

今天在开发一个HarmonyOS NEXT的应用的时候&#xff0c;发现http接口如果返回的状态码是424时&#xff0c;我在axios中定义的拦截器失效了。直接走到了业务调用的catch中。 问题表现&#xff1a; 我的拦截器代码如下&#xff1a; 解决办法&#xff1a; 先说解决办法&#xff…

在Windows上读写Linux磁盘镜像的一种方法

背景 嵌入式开发中&#xff0c;经常会把系统的Linux磁盘镜像保存到Windows上&#xff0c;以便上传到网盘备份或发送给工厂&#xff0c;但是如果想读取/修改镜像中的某个文件&#xff0c;一般有2种方案&#xff1a; 直接访问 就是用虚拟磁盘软件将镜像文件挂载成磁盘&#xf…

ffmpeg之显示一个yuv照片

显示YUV图片的步骤 1.初始化SDL库 目的&#xff1a;确保SDL库正确初始化&#xff0c;以便可以使用其窗口、渲染和事件处理功能。操作&#xff1a;调用 SDL_Init(SDL_INIT_VIDEO) 来初始化SDL的视频子系统。 2.创建窗口用于显示YUV图像&#xff1a; 目的&#xff1a;创建一个…

Windows下播放文件作为麦克风声源的一种方式

近期测试一种外语的ASR识别成功率&#xff0c;样本素材是懂这门语言的同事录制的mp3文件。测试client端原本是从麦克风拾音生成媒体流的。 这样&#xff0c;就需要想办法把mp3文件转换为测试client的输入声音。物理方式上&#xff0c;可以用一根音频线&#xff0c;把电…

如何在网页端使用 IDE 高效地阅读 GitHub 源码?

如何在网页端使用 IDE 高效地阅读 GitHub 源码&#xff1f; 前言什么是 GitHub1s&#xff1f;使用 GitHub1s 阅读 browser-use 项目源码步骤 1: 打开 GitHub 项目页面步骤 2: 修改 URL 使用 GitHub1s步骤 3: 浏览文件结构步骤 4: 使用代码高亮和智能补全功能步骤 5: 快速跳转和…

Microsoft word@【标题样式】应用不生效(主要表现为在导航窗格不显示)

背景 随笔。Microsoft word 2013基础使用&#xff0c;仅做参考和积累。 问题 Microsoft word 2013&#xff0c;对段落标题文字应用【标题样式】不生效&#xff08;主要表现为在导航窗格不显示&#xff09;。 图1 图2 观察图1和图2&#xff0c;发现图1的文字在应用【标题一】样…

2021.12.28基于UDP同信的相关流程

作业 1、将TCP的CS模型再敲一遍 服务器 #include <myhead.h> #define PORT 8888 #define IP "192.168.124.123" int main(int argc, const char *argv[]) {//创建套接字//绑定本机IP和端口号//监听客户端请求//接收客户端连接请求//收发消息//创建套接字int…