当前位置: 首页 > news >正文

electron使用electron-builder进行MacOS的 打包、签名、公证、上架、自动更新

一、前言

由于electron在macOS下的坑太多,本文不可能把所有的问题都列出来,也不可能把所有的解决方案贴出来;本文也不太会讲解每一个配置点为什么要这么设置的原因,因为有些点我也说不清,我尽可能会说明的。所以,你要抛弃你之前所有已经完成的东西,最好弄一个全新的系统,严格按照本文的步骤做。

建议用vmware虚拟机,装一个全新的macOS系统。配合vmware的拍照功能,如果中途出错,可以回退到虚拟机的上一个正常状态,非常方便。

我也是在这篇文章以及网友的帮助下才走通这个流程的,你可以参考一下。

Electron-builder 构建MacOS应用小白教程(打包 & 签名 & 公证 & 上架) - 掘金

二、系统配置

如果你需要这个环境,请私信我,200元送你(如果你需要,请在csdn上私信我,或者评论区留言,我会联系你的)。

我也是从别人那花钱买来的,另外我也是花钱各种请教,才总结出来的经验。当然,文档当中,我也会尽可能把我的经验放进去。

  • VMware® Workstation 17 Pro(17.0.0 build)

  • macOS Sonoma (版本14.0 Beta版) 

  • Xcode (Version 15.1) 

三、基本信息假设

由于配置需要一些信息,现对一些配置项做出假设

1、您的appleId账号是 996@qq.com

https://idmsa.apple.com/

https://developer.apple.com

2、加入 苹果应用开发者

这玩意需要交钱,按年付费,现在没法给大家截图了。只记得当时的坑是,得用比较新的 iphone 或者 mac,下载官方提供的一个应用,在里面注册提交资料即可。

https://developer.apple.com/programs/

3、创建appleId的 app 专用密码

相关资料见:使用 App 专用密码 - 官方 Apple 支持 (中国)

(1) 打开网址,并登录

Manage your Apple ID

(2) Manage your Apple ID

在此创建

(3) 假设我们创建的密码为:aaaa-bbbb-cccc-dddd

四、证书申请

1、生成 证书签名请求证书 文件

(1)按以下顺序,会在桌面生成一个 “CertificateSigningRequest.certSigningRequest” ,注意保存好该文件,以后要用的到。

(2)与此同时,会在钥匙串中生成一对密钥,这就是公钥和私钥,很重要,尤其私钥,如图所示

将这两个密钥导出后保存下来。这个私钥导出后,可以给别的开发人员用。别的开发人员导入这个“私钥文件和证书(.cer后缀的文件,在本文的后面会生成)文件”后,就可以在他的电脑上打包了。

下图就是本次生成的 证书签名请求文件、公钥、私钥文件

关于证书的知识,看这个文章:

https://www.jianshu.com/p/1e5f04ad396c/

https://www.jianshu.com/p/81610614225d

2、创建证书 Certificates

注意,不要在xcode里面创建证书(新版本的xcode里面创建不了 Mac Installer Distribution 证书;且不要用Apple Distribution 证书)

打开网址:

https://developer.apple.com/account

按以下步骤生成五个证书

将证书下载好后,按如下名称命名,以后要用。

3、创建 appId (即 Identifiers )

此处创建的:

bundle ID:cn.kafaka.greader

我自己账号的TeamID:GN5XP7HD2C 

上面两个合起来,就是 appId: GN5XP7HD2C.cn.kafaka.greader (注意后面的打包配置文件中有个appId配置项,不一定完全是这个,我会在那个配置文件中做 特别 说明)

4、添加 Devices

在electron-builder中,可以打包出mas和mas-dev两个目标项。mas打包出来的 pkg 要上传到 应用商店 Mac App Store(简称 mas);mas-dev打包出来的 app,要做本地测试,如果该app能正常运行,那么mas中打包出来的pkg就没问题。

而打包mas-dev时,要在应用中嵌入设备的uuid,该uuid会在下一步创建Profile时用到。

这里获取到的 uuid是 :564DBF37-1976-5C42-B5B9-584D407DB380

如果截图中的 硬件 UUID 和 预置UUID不一样,那就用 硬件uuid。如果实在不行,你把两个uuid都添加到 Devices中。

5、创建 Profiles 

按以下步骤生成3个Profiles

五、项目基本配置

1、图标生成

你搜索一个 electron-icon-builder 关键词吧,能找到的

2、plist文件

(1)entitlements.mac.inherit.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict><key>com.apple.security.app-sandbox</key><true/><key>com.apple.security.inherit</key><true/>
</dict>
</plist>

(2)entitlements.mac.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict><key>com.apple.security.cs.allow-jit</key><true/><key>com.apple.security.cs.allow-unsigned-executable-memory</key><true/><key>com.apple.security.cs.allow-dyld-environment-variables</key><true/>
</dict>
</plist>

 (3)entitlements.mas.inherit.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict><key>com.apple.security.app-sandbox</key><true/><key>com.apple.security.inherit</key><true/>
</dict>
</plist>

(4)entitlements.mas.plist

注意,本文件中的 三个部分,得更改为你自己的。该文件中的其它部分内容,你可以根据自己的情况修改。

    <key>com.apple.security.application-groups</key>
    <array>
        <string>GN5XP7HD2C.cn.kafaka.greader</string>
    </array>


    <key>com.apple.application-identifier</key>
    <string>GN5XP7HD2C.cn.kafaka.greader</string>

    <key>com.apple.developer.team-identifier</key>
    <string>GN5XP7HD2C</string>

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict><key>com.apple.security.cs.allow-jit</key><true/><key>com.apple.security.cs.allow-unsigned-executable-memory</key><true/><key>com.apple.security.cs.allow-dyld-environment-variables</key><true/><key>com.apple.security.network.client</key><true/><key>com.apple.security.network.server</key><true/><key>com.apple.security.files.user-selected.read-write</key><true/><key>com.apple.security.app-sandbox</key><true/><key>com.apple.security.application-groups</key><array><string>GN5XP7HD2C.cn.kafaka.greader</string></array><key>com.apple.application-identifier</key><string>GN5XP7HD2C.cn.kafaka.greader</string><key>com.apple.developer.team-identifier</key><string>GN5XP7HD2C</string><key>com.apple.security.files.bookmarks.app-scope</key><true/><key>com.apple.security.files.bookmarks.document-scope</key><true/>
</dict>
</plist>

你可以根据自己的情况,可添加以下内容

         // 看名字就知道与 用户选则文件相关
        <key>com.apple.security.files.user-selected.read-only</key>
        <true/>
        <key>com.apple.security.files.user-selected.read-write</key>
        <true/>

        // 看名字就知道与 下载相关
        <key>com.apple.security.files.downloads.read-write</key>
        <true/>

        // 看名字就知道与 网络访问相关

       <key>com.apple.security.network.client</key>
        <true/>
        <key>com.apple.security.network.server</key>
        <true/>

3、package.json

注意其中使用的 electron 和 electron-builder(高版本的已经直接把签名 公证等功能集成到一块了,比较省事;如果你用底版本,可能需要自己添加各种包,可能会出现其它问题)的版本号

    "devDependencies": {
        "electron": "^28.0.0",
        "electron-builder": "^24.9.1"
    },

关于会被打包的文件:dependencies会自动打包进去的;devDependencies不会打包进去,只是在开发时执行各种打包命令用的;builder中的files选项,用来指定哪些文件打包进去, 如果不指定,可能会把项目根目录下的所有文件打包进去。

打包时,electron-builder 会自动下载 electron版本,会缓存在以下目录中,可以自行下载好后放进去:

https://registry.npmmirror.com/binary.html?path=electron/

Linux: $XDG_CACHE_HOME or ~/.cache/electron/
MacOS: ~/Library/Caches/electron/
Windows: %LOCALAPPDATA%/electron/Cache or ~/AppData/Local/electron/Cache/

{"name": "electron-quick-start","version": "1.3.2","description": "A minimal Electron application","main": "src/main.js","scripts": {"start": "electron .","pack": "electron-builder --dir","dist": "electron-builder","postinstall": "electron-builder install-app-deps"},"repository": "https://github.com/electron/electron-quick-start","keywords": ["Electron","quick","start","tutorial","demo"],"author": "GitHub","license": "CC0-1.0","devDependencies": {"electron": "^28.0.0","electron-builder": "^24.9.1"},"dependencies": {},"build": {"appId": "cn.kafaka.greader","files": ["src"],"mac": {"category": "public.app-category.utilities","target": [{"target": "mas","arch": "universal"}],"icon": "./build/icons/icon.icns"}}
}

4、项目结构预览

请注意:build里面是存放一些打包资源的,electron-builder未配置时,默认从这个文件夹中找一些配置英,所以不要改这个文件夹名字。

六、打包 mas

1、package.json

注意:

(1)看好mac、mas、dmg部分的配置,如果你没成功,就仔细对比和我的区别,配置的项不要多、不要少、位置也不要变。

(2)不要自作聪明,自认为加一点配置或者少一点配置就没问题,因为有些坑,我实在说不完,所以就不说了,你照着配置就好

{"name": "electron-quick-start","version": "1.3.2","description": "A minimal Electron application","main": "src/main.js","scripts": {"start": "electron .","pack": "electron-builder --dir","dist": "electron-builder","postinstall": "electron-builder install-app-deps"},"repository": "https://github.com/electron/electron-quick-start","keywords": ["Electron","quick","start","tutorial","demo"],"author": "GitHub","license": "CC0-1.0","devDependencies": {"electron": "^28.0.0","electron-builder": "^24.9.1"},"dependencies": {},"build": {"appId": "cn.kafaka.greader","files": ["src"],"mac": {"category": "public.app-category.utilities","target": [{"target": "mas","arch": "universal"}],"icon": "./build/icons/icon.icns"},"mas": {"identity": "penghui deng (GN5XP7HD2C)","hardenedRuntime": false,"gatekeeperAssess": false,"entitlements": "./build/entitlements.mas.plist","entitlementsInherit": "./build/entitlements.mas.inherit.plist","provisioningProfile": "./build/greader_mac_app_distribution.provisionprofile"},"dmg": {}}
}

2、安装证书

将以下3个证书安装(双击cer文件运行,或者 把cer文件拖拽到 钥匙串访问 应用中)到钥匙串 的"登录"中。

安装好后,要包含以下3个。

证书安装成功后,有每个证书有以下两个特点:

(1)看到没,证书前面有个“小三角”,说明证书关联到 证书密钥的;密钥就是前面创建的 证书签名请求 文件: “CertificateSigningRequest.certSigningRequest”

(2)再看,点击证书后,上面有个“此证书有效”。如果不是这个结果,你得装该证书的的“签发者”证书,把下面4个证书下载下来装上。下载地址:https://developer.apple.com/account/resources/certificates/add

3、打包

(1)确保系统日期没问题,系统日期是 中国大陆区,时间和 北京时间是一样的,如果要修正日期,可用命令行:

方法一:调整时区,让系统自动调整。
sudo systemsetup -settimezone "Asia/Shanghai"如果上面的命令没有把日期调整过来,你可以试着重启系统。方法二:如果方法一不行,那就得强制手动改时间了。
sudo systemsetup -settimezone "Asia/Shanghai"
sudo systemsetup -setusingnetworktime off
sudo date <月><日><小时><分钟><年>// 例如:要将日期设置为 2023 年 8 月 8 日,时间为下午 2 点 30 分,您可以执行以下命令:sudo date 080214302023sudo systemsetup -setusingnetworktime on

如果时间不正确,可能出现如图所示的 时间戳问题

(2) 在项目根目录下执行这个命令

chmod -R a+rw ./*

如果不执行,可能在 Transporter 提交时出现如下问题

(3)更改package.json 中的版本号

(4)执行以下命令进行打包

 打包运行后,命令行结果大概是这样的,注意核对输出结果中的证书应用是正确的。

 这一步也不要“公证”,公证有可能导致打包的pkg内部出问题,导致在上传到 transporter 时出现其它未知问题(这个问题,我不清楚,听前辈的经验,没错的)。

如果应用的证书不正确,可能是证书安装问题。electron-builder打包时会根据一定规则选择证书,如果证书选择错误,就把无关的证书删除掉。

如果弹出如下框,一般是你的操作系统的登录密码(如果你对该 “登录”钥匙串设置独立密码了,那就是你设置的那个密码)

(5) 上传应用

打开 transporter 应用,如果没有就在 Mac App Store中下载

将dist\mas-universal(你的有可能是dist\mas 目录)目录中的pkg文件上传到 transporter中

上传交付后,大概如下所示

4、常见问题

(1)问题1

去掉 mac下面的 notarize 配置

七、打包mas-dev

这个是包含了deviceid的,即打包好后可以发给目标设置进行测试。好比你有不同版本的macOS操作系统,你可以将他们的uuid收集起来,打包好后,发给他们,以便测试你的软件是否在其它操作系统上正常运行。如果可以正常运行,那么打包的mas上传到应用商店后,也能正常运行的。

1、package.json

{"name": "electron-quick-start","version": "1.5.2","description": "A minimal Electron application","main": "src/main.js","scripts": {"start": "electron .","pack": "electron-builder --dir","dist": "electron-builder","postinstall": "electron-builder install-app-deps"},"repository": "https://github.com/electron/electron-quick-start","keywords": ["Electron","quick","start","tutorial","demo"],"author": "GitHub","license": "CC0-1.0","devDependencies": {"electron": "^28.0.0","electron-builder": "^24.9.1"},"dependencies": {},"build": {"appId": "cn.kafaka.greader","files": ["src"],"mac": {"category": "public.app-category.utilities","target": [{"target": "mas-dev","arch": "universal"}],"icon": "./build/icons/icon.icns"},"masDev": {"identity": "penghui deng (8MUK7LXZQ5)","hardenedRuntime": false,"gatekeeperAssess": false,"entitlements": "./build/entitlements.mas.plist","entitlementsInherit": "./build/entitlements.mas.inherit.plist","provisioningProfile": "./build/greader_mac_app_development.provisionprofile"},"dmg": {}}
}

2、安装证书

3、打包

npm run dist

八、打包dmg

你也可以打包成dmg格式,这个格式可以发布到你的官网,以便其它人使用。不过这个格式,需要 进行“公证”,否则别人安装时会提示错误。

1、 package.json

注意

(1)appId部分,和mas的配置是一样的(如果打包后闪退,可以在前添加 teamId ,即 改成  GN5XP7HD2C.cn.kafaka.greader)

(2)mac下有"notarize" 部分设置,这个是为了 进行“公证”

{"name": "electron-quick-start","version": "1.0.0","description": "A minimal Electron application","main": "main.js","scripts": {"start": "electron .","pack": "electron-builder --dir","dist": "electron-builder","postinstall": "electron-builder install-app-deps"},"repository": "https://github.com/electron/electron-quick-start","keywords": ["Electron","quick","start","tutorial","demo"],"author": "GitHub","license": "CC0-1.0","devDependencies": {"electron": "^28.0.0","electron-builder": "^24.9.1"},"dependencies": {},"build": {"appId": "cn.kafaka.greader","files": ["src"],"mac": {"category": "public.app-category.utilities","target": ["dmg"],"icon": "./build/icons/icon.icns","hardenedRuntime": true,"gatekeeperAssess": false,"entitlements": "./build/entitlements.mas.plist","entitlementsInherit": "./build/entitlements.mas.inherit.plist","provisioningProfile": "./build/greader_developer_id_application.provisionprofile","notarize": {"teamId": "GN5XP7HD2C"}},"dmg": {         }}
}

2、 安装证书

得安装如下图的所示的证书

3、打包

打包命令行,其中的两个export  是设置环境变量,设置好后,打包时会自动公证。

APPLE_APP_SPECIFIC_PASSWORD 参见前面的章节

export APPLE_ID=996@qq.com
export APPLE_APP_SPECIFIC_PASSWORD=aaaa-bbbb-cccc-dddd
npm run dist

下图为打包结果,其中 notarization successful 即提示已公证成功

九、打包zip

zip其实就是把 .app 格式做成一个压缩包,解压后释放出 .app 格式的文件,然后双击直接运行。

注意:在macOS上,.app 格式的其实就是一个文件夹,只是操作系统会把名字以.app结尾的文件夹识别别一个 macOS应用程序而已。

这个格式也需要进行“公证”,否则别人安装时也会提示错误。

这个同 打包dmg 一模一样,只是在 target 中添加一个 “zip”即可。

十、异常分析

1、运行打包的app时,出现异常,查看原因

看下图图片,红色线条框起来的部分,是异常类型和原因说明,例如下图:大概意思是 无效签名。这就说明,你的证书有问题,需要重新从 开者发王者下载,然后重新部署到mac上。

十一、自动更新

1、安装electron-updater

npm i electron-updater
npm i electron-log

2、package.json 配置

        得添加下一行,值随意。添加这一行后,才会生成 latest.yml 文件

         "repository": "https://github.com/electron/electron-quick-start"

3、代码,在主进程中 放置如下代码 main.js 代码

将 前面打包的 文件放到服务器目录中:

http://www.xxx.com/release/

(1)对于windows,请打包出nsis,将如下图所示的3个文件放到服务器端

(2)对于macOS,请打包出,要公证,将如下图所示的3个文件放到服务器端

(3)对于Linux,请打包出,要公证,将如下图所示的3个文件放到服务器端

// Modules to control application life and create native browser window
const { app, BrowserWindow, dialog, ipcMain } = require('electron')
const path = require('node:path')
const log = require('electron-log');
const { autoUpdater } = require('electron-updater');/*
// 为啥要用?看看mac吧
app.commandLine.appendSwitch('no-sandbox');
app.commandLine.appendSwitch('disable-gpu');
app.commandLine.appendSwitch('disable-software-rasterizer');
app.commandLine.appendSwitch('disable-gpu-compositing');
app.commandLine.appendSwitch('disable-gpu-rasterization');
app.commandLine.appendSwitch('disable-gpu-sandbox');
app.commandLine.appendSwitch('--no-sandbox');
*///报错:ERROR:gpu_init.cc(481)] 修复,禁用硬件加速
app.disableHardwareAcceleration();/*
日志输出目录:
on Linux: ~/.config/<app name>/log.log
on macOS: ~/Library/Logs/<app name>/log.log
on Windows: %USERPROFILE%\AppData\Roaming\<app name>\logs\log.log
*/
log.transports.file.level = true; //是否输出到 日志文件
log.transports.console.level = true; //是否输出到 控制台
var logFilePath = log.transports.file.getFile();
log.error("log_file_path=" + logFilePath);/*
log.error('error!');
log.info('info!');
log.warn('warn!');
log.verbose('verbose!');
log.debug('debug!');
log.silly('silly!');
*/function createWindow() {// Create the browser window.const mainWindow = new BrowserWindow({width: 800,height: 600,webPreferences: {preload: path.join(__dirname, 'preload.js')}})// and load the index.html of the app.mainWindow.loadFile(path.join(__dirname, 'index.html'))// Open the DevTools.// mainWindow.webContents.openDevTools()return mainWindow;
}ipcMain.handle('toCheckUpdate', (e) => {checkAppVersionUpdate()
});/*** 从服务器检测 新版本,并获取更新地址*/
function checkAppVersionUpdate() {// 从服务器获取 更新地址,var G_UpdateDirUrl = "http://www.xxx.com/release/";doAppVersionUpdate(G_UpdateDirUrl);
}function doAppVersionUpdate(updateDirUrl) {// 检查更新,相关接口请查阅网址:https://blog.csdn.net/DAJJbb/article/details/126767869/*macOS: DMG.Linux: AppImage, DEB and RPM.Windows: NSIS.*/// 设置是否自动下载,默认是true,当点击检测到新版本时,会自动下载安装包autoUpdater.autoDownload = true;// 如果安装包下载好了,当应用退出后是否自动安装更新autoUpdater.autoInstallOnAppQuit = false;autoUpdater.logger = log;// 设置版本更新服务器地址autoUpdater.setFeedURL(updateDirUrl);// 开始检查更新事件autoUpdater.on('checking-for-update', function () {log.info('checking-for-update');})// 没有可更新版本autoUpdater.on('update-not-available', function (info) {log.info('update-not-available');})// 发现可更新版本autoUpdater.on('update-available', function (info) {log.info('update-available');})// 更新下载进度事件autoUpdater.on('download-progress', function (progressObj) {log.info('download-progress');// mainWindow.webContents.send('on-soft-download', progressObj.percent);});// 下载完成autoUpdater.on('update-downloaded', function (event, releaseNotes, releaseName, releaseDate, updateUrl, quitAndUpdate) {// mainWindow.webContents.send('on-soft-download', 100);// 已经下载完成,询问用户是否退出更新const dialogOpts = {type: 'info',buttons: ["重启", "以后更新"],title: "版本更新",message: "检测到新版本,是否更新"};dialog.showMessageBox(dialogOpts).then((returnValue) => {if (returnValue.response === 0) {autoUpdater.quitAndInstall();}});});// 检查更新/*setInterval(() => {autoUpdater.checkForUpdates()}, 1000 * 60 * 5);*/autoUpdater.checkForUpdates();
}// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(() => {createWindow()app.on('activate', function () {// On macOS it's common to re-create a window in the app when the// dock icon is clicked and there are no other windows open.if (BrowserWindow.getAllWindows().length === 0) createWindow()})// 15秒钟后,开始检测更新setTimeout(function () { checkAppVersionUpdate(); }, 1000 * 15);
})// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', function () {if (process.platform !== 'darwin') app.quit()
})// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.

其它图片备份

相关文章:

electron使用electron-builder进行MacOS的 打包、签名、公证、上架、自动更新

一、前言 由于electron在macOS下的坑太多&#xff0c;本文不可能把所有的问题都列出来&#xff0c;也不可能把所有的解决方案贴出来&#xff1b;本文也不太会讲解每一个配置点为什么要这么设置的原因&#xff0c;因为有些点我也说不清&#xff0c;我尽可能会说明的。所以&…...

RAD Studio 12 安装激活说明及常见问题

目录 RAD Studio 安装说明 RAD Studio 最新的修补程序更新 RAD Studio 产品相关信息 Embarcadero 产品在线注册步骤 单机版授权产品注册注意事项 Embarcadero 产品离线注册步骤 Embarcadero 产品安装次数查询 Embarcadero 序号注册次数限制 EDN账号 - 查询授权序号、下…...

JavaScript实现视频共享

1.视频共享webrtc-master index.html <!DOCTYPE html> <html> <head><script typetext/javascript srchttps://cdn.scaledrone.com/scaledrone.min.js></script><meta charset"utf-8"><meta name"viewport" cont…...

uniapp框架——vue3+uniFilePicker+fastapi实现文件上传(搭建ai项目第二步)

文章目录 ⭐前言&#x1f496; 小程序系列文章 ⭐uni-file-picker 组件&#x1f496; 绑定事件&#x1f496; uploadFile api&#x1f496; 自定义上传 ⭐后端fastapi定义上传接口⭐uniapp开启本地请求代理devServer⭐前后端联调⭐总结⭐结束 ⭐前言 大家好&#xff0c;我是ym…...

一篇文章带你入门PHP魔术方法

PHP魔术方法 PHP 中的"魔术方法"是一组特殊的方法&#xff0c;它们在特定情况下自动被调用。这些方法的名称都是以两个下划线&#xff08;__&#xff09;开头。魔术方法提供了一种方式来执行各种高级编程技巧&#xff0c;使得对象的行为可以更加灵活和强大。以下是一…...

【数据库系统概论】第6章-关系数据库理论

真别看吧&#xff0c;抄ppt而已啊 文章目录 6.1 引言6.2 规范化6.2.1 函数依赖6.2.2 码6.2.3 范式&#xff08;Normal Form&#xff09;6.2.4 BC范式6.2.5 规范化小结 6.1 引言 我们有这样一张表&#xff1a; but 为啥这样设计呢&#xff1f;由此引出怎样设计一个关系数据库…...

算法设计与分析实验报告-贪心算法

校课程的简单实验报告。 算法设计与分析实验报告-递归与分治策略 算法设计与分析实验报告-动态规划算法 算法设计与分析实验报告-贪心算法 dijkstra迪杰斯特拉算法&#xff08;邻接表法&#xff09; 算法设计与分析实验报告-回溯法 算法设计与分析实验报告-分支限界法 …...

Unity读取服务器声音文件

Unity读取服务器声音文件 功能1.在网站的根目录放置一个声音文件Alarm01.wav&#xff08;这个是window系统自带的找不到这个格式的可以直接在C盘搜索&#xff09;2.在WebManager.cs脚本中添加clipPath、audio、m_downloadClip属性和DownloadSound&#xff08;&#xff09;函数&…...

掌握ElasticSearch(一):Elasticsearch安装与配置、Kibana安装

文章目录 〇、简介1.Elasticsearch简介2.典型业务场景3.数据采集工具4.名词解释 一、安装1.使用docker(1)创建虚拟网络(2)Elasticsearch安装步骤 2.使用压缩包 二、配置1.目录介绍2.配置文件介绍3.elasticsearch.yml节点配置4.jvm.options堆配置 二、可视化工具Kibana1.介绍2.安…...

《剑指offer》Java版--13.机器人的运动范围(BFS)

剑指offer原题13:机器人的运动范围 地上有一个m行n列的方格。一个机器人从坐标(0,0)的格子开始移动&#xff0c;它每次可以向左、右、上、下移动一格&#xff0c;但不能进入行坐标和列坐标的数位之和大于k的格子。例如&#xff0c;当k为18时,机器人能够进入方格(35,37),因为353…...

基于流程挖掘的保险理赔优化策略实践

引言 在当今日益竞争的商业环境中,保险公司面临着日益增长的业务量和客户期望的挑战。特别是在理赔领域,理赔是保险行业的重要环节,也是保险公司和客户之间最直接的联系点。然而,长周期和繁琐的理赔流程常常给保险公司和投保人带来困扰。因此,如何提供准确且高效的理赔处…...

Docker五 | DockerFile

目录 DockerFile 常用保留字 FROM MAINTAINER RUN EXPOSE WORKDIR USER ENV VOLUME ADD COPY CMD ENTRYPOINT DockerFile案例 前期准备 编写DockerFile文件 运行DockerFile 运行镜像 DockerFile是用来构建Docker镜像的文本文件&#xff0c;是由一条条构建…...

2023年度总结:技术旅程的杨帆远航⛵

文章目录 职业规划与心灵成长 ❤️‍&#x1f525;我的最大收获与成长 &#x1f4aa;新年Flag &#x1f6a9;我的技术发展规划 ⌛对技术行业的深度思考 &#x1f914;祝愿 &#x1f307; 2023 年对我来说是一个充实而令人难以忘怀的一年。这一年&#xff0c;我在CSDN上发表了 1…...

SpringBoot+AOP+Redis 防止重复请求提交

本文项目基于以下教程的代码版本&#xff1a; https://javaxbfs.blog.csdn.net/article/details/135224261 代码仓库: springboot一些案例的整合_1: springboot一些案例的整合 1、实现步骤 2.引入依赖 我们需要redis、aop的依赖。 <dependency><groupId>org.spr…...

偷流量、端口占用、网络负载高、socket创建释放异常等Android高阶TCP/IP网络问题定位思路

一&#xff0c;背景 通常一些偷流量、端口占用、网络负载高、socket创建释放异常等Android网络相关问题&#xff0c;可以通过使用tcpdump抓tcp/ip报文&#xff0c;来定位。但是tcpdump无进程信息&#xff0c;也没有APK包名信息&#xff0c;无法确认异常的报文来自哪些Apk或者n…...

《人人都能用英语》学习笔记

https://github.com/xiaolai/everyone-can-use-english 核心&#xff1a; 用 What──它究竟是什么&#xff1f;Why──为什么它是那个样子&#xff1f;How──要掌握它、应用它&#xff0c;必须得遵循什么样的步骤&#xff1f; 在运行程序之前&#xff0c;要反复浏览代码&a…...

NFC与ZigBee技术在智慧农业物联网监测系统中的应用

近年来&#xff0c;我国农业物联网技术飞速发展&#xff0c;基于物联网技术的智能农业监测系统有望得到较大规模的推广应用。但传统的物联网农业监测系统其网络结构层次单一&#xff0c;多采用基于有线或无线结构的节点-上位机数据采集模式&#xff0c;节点数据访问模式缺乏灵活…...

k8s-cni网络 10

Flannel vxlan模式跨主机通信原理 在同一个节点上的pod 流量通过cni网桥可以直接进行转发&#xff1b; 在需要跨主机访问时&#xff0c;数据包通过flannel(隧道) 知道另一边的mac地址&#xff0c;就可以拿到另一边的ip地址&#xff0c;然后构建常规的以太网数据包&#xff0c;…...

听GPT 讲Rust源代码--src/tools(27)

File: rust/src/tools/clippy/clippy_lints/src/methods/suspicious_to_owned.rs 文件rust/src/tools/clippy/clippy_lints/src/methods/suspicious_to_owned.rs的作用是实施Clippy lint规则&#xff0c;检测产生潜在性能问题的字符转换代码&#xff0c;并给出相关建议。 在Rus…...

经济危机下,我们普通人如何翻身?2024创业新风口,适合普通人的创业项目

明年的商业环境会比今年更残酷&#xff0c;不是贩卖危机。旅游行业还在刺激性消费&#xff0c;再过几个月大家就没钱了&#xff0c;估计慢慢也消停。中小微企业资金链断裂&#xff0c;大部分公司倒闭&#xff0c;大批人失业&#xff0c;所以经济恢复需要一个周期。 30年河东&am…...

深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing) 第五节 引用类型复制问题及用克隆接口ICloneable修复

深入浅出图解C#堆与栈 C# Heaping VS Stacking 第五节 引用类型复制问题及用克隆接口ICloneable修复 [深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing) 第一节 理解堆与栈](https://mp.csdn.net/mdeditor/101021023)[深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing) 第二节…...

python中基本元素的pop函数

python中基本元素的pop函数 一、列表List二、元组Tuple三、字典dict四、集合set 一、列表List pop() 根据索引删除并返回被删除的元素&#xff0c;索引默认为-1 a [1, 2, 3, 2, 5] b a.pop() # b5&#xff0c;默认返回最后一个值 print(b) b a.pop(2) # b3,返回a[2] pri…...

MPLS动态协议LDP配置示例

一、预习&#xff1a; MPLS是一种根据报文中携带的标签来转发数据的技术&#xff0c;两台LSR必须在它们之间转的数据 的标签使用上“达成共识”。LSR之间可以运行LDP来告知其他LSR本设备上的标签绑定信息&#xff0c;从而实现标签报文的正确转发。 LSR&#xff1a;Label Switch…...

JS调用栈:为何会栈溢出

JS调用栈&#xff1a;为何会栈溢出 JS调用栈什么是函数调用什么是栈在开发中利用调用栈栈溢出 JS调用栈 JavaScript 经常会出现一个函数中调用另外一个函数的情况&#xff0c;调用栈就是用来管理函数调用关系的一种数据结构&#xff0c;首先你要先弄明白函数调用和栈结构 什么…...

代码随想Day52 | 300.最长递增子序列、674. 最长连续递增序列、718. 最长重复子数组

300.最长递增子序列 这道题目的重点在于动态数组的定义 dp[i]&#xff1a;以nums[i]为结尾的最长递增子序列&#xff0c;因为这样定义可以进行递推&#xff1b; 递推&#xff1a;j从0-i进行对比&#xff0c;如果nums[i]大于nums[j]&#xff0c;dp[i]dp[j]1&#xff1b; 初始化…...

使用 pytest 相关特性重构 appium_helloworld

一、前置说明 在 pytest 基础讲解 章节,介绍了 pytest 的特性和基本用法,现在我们可以使用 pytest 的一些机制,来重构 appium_helloworld 。 appium_helloworld 链接: 编写第一个APP自动化脚本 appium_helloworld ,将脚本跑起来 代码目录结构: pytest.ini 设置: [pyt…...

猪目标检测数据集VOC格式600张

猪是一种常见的哺乳动物&#xff0c;通常被人们认为是肉食动物&#xff0c;但实际上猪是杂食性动物&#xff0c;以植物性食物为主&#xff0c;也有偶尔食肉的习性。猪的体型较大&#xff0c;圆胖的体型和圆润的脸庞使其显得憨态可掬。它们主要通过嗅觉来感知周围环境&#xff0…...

Pandas中concat的用法

Pandas中concat的用法 ​ pd.concat 是 pandas 库中的一个函数&#xff0c;用于将多个 pandas 对象&#xff08;如 Series、DataFrame&#xff09;沿指定轴进行合并连接。 pd.concat(objs, axis0, joinouter, ignore_indexFalse, keysNone, levelsNone, namesNone, verify_in…...

【C++】引用详解

前言 在学习C语言时&#xff0c;我们通常会遇到两个数交换的问题&#xff0c;为了实现这一功能&#xff0c;我们会编写一个经典的Swap函数&#xff0c;如下所示&#xff1a; void Swap(int *a, int *b) {int tmp *a;*a *b;*b tmp; } 然而&#xff0c;这个Swap函数看起来可…...

平时的一些思考内容

文章目录 阶乘位运算求概率 阶乘 阶乘是一很迷人的&#xff0c;刚开始的的变化还不是很大&#xff0c;到后面变化类似于直线上升的&#xff0c;不知道现实中哪些实例来表示阶乘。19的阶乘就已经超过了long了&#xff0c;在竞赛或者其他中要求2023或者很大数字的阶乘就需要考虑…...

教育网站建设的策划方案/成都关键词优化报价

基督教 赞美诗歌 Hymns Lyrics MP3 中文版 英文版 中英对照 MP3音频提取&#xff08;自动播放&#xff09;&#xff1a; http://filer.blogbus.com/5320797/resource_53207971262594667h.mp3 &#xff08;右击迅雷或目标另存为&#xff09; 词曲&#xff1a; 写作背景介绍&…...

那个网站做足球测/建站模板网站

人生态度一般来说主要由()基本要素组成。A、人生认知B、人生情感C、人生意向D、人生目标职业目标包括总体目标和一个个阶段性目标&#xff0c;要善于把总体目标分解成一个个阶段性的目标&#xff0c;职务目标美好的人生价值目标靠()才能化为现实。A、客观条件B、主观条件C、社会…...

手机网站建设设计/百度推广中心

I值便利, 什么是I值便利, 我们可以把I当成一个整型的首字母, 在这里有两种方法可以实现. 涉及到的方法: objectAtIndex: 这里指的是打印数组里的第几个元素. 第一种方法是直接打印数组里有多少个元素. 第二种是直接把数组里的所有元素逐个打印出来. #import <Foundation/Fou…...

wordpress文章调用标签/哪些行业适合做网络推广

http://releases.ubuntu.com/12.04/...

章丘建网站/广州网站营销优化qq

一、SGI 标准的空间配置器&#xff0c;std::allocator SGI也定义了一个符合部分标准&#xff0c;名为allocator的配置器&#xff0c;但是它自己不使用&#xff0c;也不建议我们使用&#xff0c;主要原因是效率不佳。 它只是把C的操作符::operator new和::operator delete做了一…...

如何在自己电脑上做网站服务器/数据网站

2.1 问题 创建randpass.py脚本&#xff0c;要求如下&#xff1a; 编写一个能生成8位随机密码的程序 使用random的choice函数随机取出字符 改进程序&#xff0c;用户可以自己决定生成多少位的密码 2.2 方案 导入random模块&#xff0c;通过random静态对象调用choice()方法&…...