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

在Electron中实现实时下载进度显示的完整指南

在开发Electron应用时,提供良好的用户体验至关重要,尤其是在下载大文件时。用户需要知道下载进度、预计完成时间以及当前下载速度。本文将详细介绍如何在Electron应用中实现实时下载进度显示功能,从主进程到渲染进程的完整流程。
技术栈是electron+vue3作为示例,其它的技术栈同样可以使用

系统架构概述

实现下载进度显示功能需要以下三个主要组件协同工作:

  1. 主进程(Main Process):负责实际的文件下载和进度跟踪
  2. 预加载脚本(Preload Script):安全地暴露主进程的功能和事件给渲染进程
  3. 渲染进程(Renderer Process):负责显示下载进度界面和用户交互

下面是整个系统的工作流程:

主进程(main.js) ─┐│ IPC通信
预加载脚本(preload.js) ─┐│ 暴露API
渲染进程(App.vue + DownloadModal.vue)

实现步骤

1. 主进程中实现下载和进度跟踪

首先,在main.js中实现下载处理器,并添加进度跟踪逻辑:

// client/electron/main.js
const { app, BrowserWindow, ipcMain } = require('electron');
const fs = require('fs');
const path = require('path');
const http = require('http');
const https = require('https');// 处理下载请求
ipcMain.handle('download-update', async (event, options) => {try {const { url, filename, version } = options;if (!url || !filename) {return { success: false, error: '下载地址或文件名无效' };}// 准备下载路径,要下载到哪个目录下,userHomeDir系统的默认主目录const userHomeDir = os.homedir();const downloadDir = path.join(userHomeDir, '要下载到的目录名');// 确保目录存在if (!fs.existsSync(downloadDir)) {fs.mkdirSync(downloadDir, { recursive: true });}// 确定下载文件路径let filePath;if (process.platform === "win32") {filePath = path.join(downloadDir, '文件名.exe');} else {filePath = path.join(downloadDir, '文件名');}// 开始下载文件const result = await downloadFileWithProgress(event, url, filePath);return result;} catch (error) {console.error('下载失败:', error);return { success: false, error: error.message };}
});// 实现带进度的下载函数
async function downloadFileWithProgress(event, url, filePath) {return new Promise((resolve, reject) => {// 根据URL选择协议const requester = url.startsWith('https') ? https : http;console.log('文件将下载到:', filePath);const request = requester.get(url, (response) => {// 处理重定向if (response.statusCode === 301 || response.statusCode === 302) {const redirectUrl = response.headers.location;console.log('下载重定向到:', redirectUrl);return resolve(downloadFileWithProgress(event, redirectUrl, filePath));}// 获取文件大小const totalSize = parseInt(response.headers['content-length'], 10);let downloadedSize = 0;let lastProgressTime = Date.now();let lastDownloadedSize = 0;// 创建文件写入流const file = fs.createWriteStream(filePath);// 监听数据接收事件,更新进度response.on('data', (chunk) => {downloadedSize += chunk.length;const percent = totalSize ? Math.round((downloadedSize / totalSize) * 100) : 0;// 计算下载速度 (每秒更新一次)const now = Date.now();const elapsedTime = now - lastProgressTime;if (elapsedTime >= 1000 || percent === 100) {const bytesPerSecond = Math.round((downloadedSize - lastDownloadedSize) / (elapsedTime / 1000));// 将下载大小格式化为可读的字符串const formattedDownloaded = formatBytes(downloadedSize);const formattedTotal = formatBytes(totalSize);const formattedSpeed = formatBytes(bytesPerSecond) + '/s';// 更新最后进度时间和大小lastProgressTime = now;lastDownloadedSize = downloadedSize;// 发送进度给渲染进程event.sender.send('download-progress', {percent: percent,downloaded: downloadedSize,total: totalSize,formattedDownloaded: formattedDownloaded,formattedTotal: formattedTotal,speed: formattedSpeed});}});// 将响应导入文件response.pipe(file);// 监听文件写入完成事件file.on('finish', () => {file.close();console.log('文件下载完成:', filePath);resolve({ success: true, filePath });});// 监听错误file.on('error', (err) => {fs.unlink(filePath, () => {});console.error('文件写入错误:', err);reject(err);});});// 处理请求错误request.on('error', (err) => {console.error('下载失败:', err);fs.unlink(filePath, () => {}); // 删除可能部分下载的文件reject(err);});});
}// 辅助函数:格式化字节大小
function formatBytes(bytes, decimals = 2) {if (bytes === 0) return '0 Bytes';const k = 1024;const dm = decimals < 0 ? 0 : decimals;const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];const i = Math.floor(Math.log(bytes) / Math.log(k));return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}

主进程实现了以下关键功能:

  1. 通过downloadFileWithProgress函数下载文件,同时跟踪进度
  2. 计算下载百分比、下载速度和格式化的文件大小
  3. 使用event.sender.send()方法向渲染进程发送实时进度更新
  4. 处理重定向、错误和完成事件
  5. 包含辅助函数formatBytes将字节大小转换为可读格式

2. 预加载脚本中暴露下载功能和事件

preload.js中,我们需要安全地暴露下载功能和进度事件给渲染进程:

// client/electron/preload.js
const { contextBridge, ipcRenderer } = require('electron');// 安全地暴露主进程功能给渲染进程
contextBridge.exposeInMainWorld('electron', {// 下载文件APIdownloadUpdate: (options) => {return ipcRenderer.invoke('download-update', options);},// 下载进度事件监听器onDownloadProgress: (callback) => {// 移除可能存在的旧监听器ipcRenderer.removeAllListeners('download-progress');// 添加新的监听器ipcRenderer.on('download-progress', (event, progressData) => {callback(progressData);});// 返回清理函数return () => {ipcRenderer.removeAllListeners('download-progress');};}
});

预加载脚本完成了两个关键任务:

  1. 暴露downloadUpdate方法,使渲染进程能够调用主进程的下载功能
  2. 暴露onDownloadProgress事件监听器,使渲染进程能够接收下载进度更新
  3. 提供清理函数,确保不会留下多余的事件监听器

3. 渲染进程中接收和处理下载进度

在App.vue中,我们需要设置状态变量和事件监听器来处理下载进度:

// client/src/App.vue
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import DownloadModal from '@/components/DownloadModal.vue';// 下载状态
const downloadState = ref({visible: false,fileName: '',url: '',version: '',percentage: 0,downloadedSize: '0 KB',totalSize: '0 MB',speed: '0 KB/s'
});// 下载对话框引用
const downloadModalRef = ref(null);// 清理函数引用
let cleanupProgressListener = null;// 组件挂载时设置进度监听器
onMounted(() => {if (window.electron && window.electron.onDownloadProgress) {cleanupProgressListener = window.electron.onDownloadProgress((progressData) => {// 更新下载状态downloadState.value.percentage = progressData.percent;downloadState.value.downloadedSize = progressData.formattedDownloaded;downloadState.value.totalSize = progressData.formattedTotal;downloadState.value.speed = progressData.speed;console.log(`下载进度: ${progressData.percent}%, 速度: ${progressData.speed}`);});}
});// 组件卸载时清理监听器
onUnmounted(() => {if (cleanupProgressListener) {cleanupProgressListener();}
});// 确认更新开始下载
const confirmUpdate = async () => {// 隐藏确认对话框updateConfirm.value.visible = false;// 重置下载状态downloadState.value = {visible: true,fileName: process.platform === 'win32' ? 'secmate.exe' : 'secmate',url: updateConfirm.value.url,version: updateConfirm.value.version,percentage: 0,downloadedSize: '0 KB',totalSize: '计算中...',speed: '0 KB/s'};// 显示下载对话框if (downloadModalRef.value) {downloadModalRef.value.startDownload();}try {if (!window.electron || !window.electron.downloadUpdate) {throw new Error('下载功能不可用');}// 开始下载const result = await window.electron.downloadUpdate({url: updateConfirm.value.url,filename: downloadState.value.fileName,version: updateConfirm.value.version});console.log('下载结果:', result);if (result.success) {// 下载成功,完成下载动画if (downloadModalRef.value) {downloadModalRef.value.completeDownload();}} else {// 下载失败showMessage('下载失败: ' + (result.error || '未知错误'), 'error');downloadState.value.visible = false;}} catch (error) {console.error('下载过程出错:', error);showMessage('下载过程出错: ' + error.message, 'error');downloadState.value.visible = false;}
};// 处理下载完成
const handleDownloadComplete = async () => {console.log('下载已完成');// 添加版本信息到成功消息const versionText = downloadState.value.version ? ` (版本 ${downloadState.value.version})` : '';showMessage(`更新文件已下载完成${versionText}`, 'success');// 关闭下载状态downloadState.value.visible = false;// 启动后端服务或其他后续操作...
};
</script><template><!-- 下载进度弹窗 --><DownloadModal:visible="downloadState.visible":fileName="downloadState.fileName":progress="downloadState.percentage":total="downloadState.totalSize":downloadState="downloadState"ref="downloadModalRef"@complete="handleDownloadComplete"/><!-- 其他组件... -->
</template>

App.vue中的关键实现包括:

  1. 创建downloadState响应式对象,存储下载状态信息
  2. 使用onMountedonUnmounted生命周期钩子管理进度事件监听器
  3. confirmUpdate函数中开始下载流程并重置下载状态
  4. 将下载状态传递给DownloadModal组件显示进度信息
  5. 通过handleDownloadComplete处理下载完成后的逻辑

4. 创建下载进度显示组件

最后,创建DownloadModal.vue组件来显示下载进度:

<!-- client/src/components/DownloadModal.vue -->
<template><div class="download-modal" v-show="visible"><div class="download-dialog"><div class="download-header"><img width="24px" height="24px" src="@/assets/zhuce.png" alt="下载" class="download-emoji"><h3>正在下载…</h3></div><div class="progress-container"><!-- 进度条 --><div class="progress-bar"><div class="progress-fill" :style="{ width: `${progress}%` }"></div></div><!-- 进度信息 --><div class="progress-stats"><div class="progress-text">{{ progress }}%</div><div class="progress-size">{{ downloadState.downloadedSize }}/{{ downloadState.totalSize }}</div></div><!-- 下载速度 --><div class="download-speed" v-if="downloadState.speed">{{ downloadState.speed }}</div></div><div class="download-message">{{ message }}</div></div></div>
</template><script setup>
import { ref, watch } from 'vue';const props = defineProps({visible: Boolean,progress: {type: Number,default: 0},fileName: String,downloadState: {type: Object,default: () => ({downloadedSize: '0 KB',totalSize: '0 MB',speed: '0 KB/s'})}
});const emit = defineEmits(['complete']);// 状态变量
const message = ref('');
let progressInterval = null;// 监听进度变化,更新提示消息
watch(() => props.progress, (newProgress) => {if (newProgress < 30) {message.value = '正在下载更新包...';} else if (newProgress < 60) {message.value = '正在下载更新包...';} else if (newProgress < 90) {message.value = '下载中,请稍候...';} else {message.value = '即将完成下载...';}
});// 开始下载动画
function startDownload() {// 清除可能存在的旧计时器if (progressInterval) clearInterval(progressInterval);// 设置初始消息message.value = '正在连接下载服务器...';
}// 完成下载
function completeDownload() {clearInterval(progressInterval);message.value = '下载完成!';// 延迟关闭setTimeout(() => {emit('complete');}, 1000);
}// 暴露方法给父组件
defineExpose({startDownload,completeDownload
});
</script><style scoped>
.download-modal {position: fixed;top: 0;left: 0;width: 100vw;height: 100vh;background-color: rgba(0, 0, 0, 0.5);display: flex;justify-content: center;align-items: center;z-index: 10000000;
}.download-dialog {width: 380px;background-color: white;border-radius: 14px;padding: 30px;box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
}.download-header {display: flex;align-items: center;margin-bottom: 20px;
}h3 {margin: 0;font-size: 18px;color: #333;
}.progress-container {margin-bottom: 16px;
}.progress-bar {height: 8px;background-color: #f0f0f0;border-radius: 4px;overflow: hidden;
}.progress-fill {height: 100%;background: linear-gradient(90deg, #7af2ff, #477cff);width: 0;border-radius: 4px;transition: width 0.3s ease;
}.progress-stats {display: flex;justify-content: space-between;margin-top: 8px;
}.progress-text, .progress-size {font-size: 14px;color: #666;
}.download-speed {text-align: right;margin-top: 4px;font-size: 13px;color: #888;
}.download-message {text-align: center;font-size: 14px;color: #555;min-height: 20px;margin-top: 10px;
}
</style>

DownloadModal组件的核心功能包括:

  1. 接收并显示下载进度、文件大小和下载速度
  2. 提供动态进度条,显示当前下载百分比
  3. 根据进度显示相应的提示消息
  4. 提供startDownloadcompleteDownload方法供父组件调用

关键技术点解析

1. 实时进度计算和格式化

在主进程中,我们不仅计算下载百分比,还计算下载速度并格式化文件大小:

// 计算下载速度
const now = Date.now();
const elapsedTime = now - lastProgressTime;if (elapsedTime >= 1000 || percent === 100) {const bytesPerSecond = Math.round((downloadedSize - lastDownloadedSize) / (elapsedTime / 1000));// 格式化大小为可读字符串const formattedDownloaded = formatBytes(downloadedSize);const formattedTotal = formatBytes(totalSize);const formattedSpeed = formatBytes(bytesPerSecond) + '/s';// 更新最后进度时间和大小lastProgressTime = now;lastDownloadedSize = downloadedSize;// 发送进度数据...
}

2. 安全的IPC通信

通过预加载脚本,我们安全地桥接了主进程和渲染进程的通信:

// 在预加载脚本中暴露事件监听器
onDownloadProgress: (callback) => {ipcRenderer.removeAllListeners('download-progress');ipcRenderer.on('download-progress', (event, progressData) => {callback(progressData);});return () => {ipcRenderer.removeAllListeners('download-progress');};
}

3. 响应式UI更新

在Vue组件中,我们使用响应式对象和计算属性来确保UI与下载状态同步:

// 通过 props 将下载状态传递给组件
<DownloadModal:visible="downloadState.visible":progress="downloadState.percentage":downloadState="downloadState"ref="downloadModalRef"@complete="handleDownloadComplete"
/>// 在组件内部监听进度变化
watch(() => props.progress, (newProgress) => {if (newProgress < 30) {message.value = '正在下载更新包...';} else if (newProgress < 60) {message.value = '正在下载更新包...';} else if (newProgress < 90) {message.value = '下载中,请稍候...';} else {message.value = '即将完成下载...';}
});

最佳实践与优化建议

  1. 节流进度更新:对于较大的文件,每个数据块都发送进度更新会导致性能问题。我们使用时间间隔(每秒更新一次)来节流进度更新。

  2. 格式化显示大小:使用formatBytes函数将字节数转换为人类可读的格式,如KB、MB和GB。

  3. 提供下载速度:显示当前下载速度,帮助用户估计剩余时间。

  4. 正确清理资源:在组件卸载时清理事件监听器,避免内存泄漏。

  5. 显示不同阶段的消息:根据下载进度显示不同的提示消息,增强用户体验。

  6. 处理下载错误:捕获并显示下载过程中的错误,提供有意义的错误信息。

  7. 保留下载历史:可以考虑添加下载历史记录功能,允许用户查看和管理历史下载。

应用场景

这种实时下载进度显示功能可以应用于多种场景:

  1. 应用自动更新:显示新版本下载进度
  2. 大文件下载:下载大型资源文件,如视频、音乐或文档
  3. 插件安装:下载和安装第三方插件或扩展
  4. 批量下载:同时下载多个文件并显示总体进度
  5. 数据导入/导出:在数据迁移过程中显示进度

总结

在Electron应用中实现实时下载进度显示是提升用户体验的重要一环。通过主进程跟踪下载进度、预加载脚本安全地暴露IPC通信,以及渲染进程中的响应式UI更新,我们可以创建一个流畅、信息丰富的下载体验。

这种架构不仅保证了安全性,还提供了良好的性能和用户体验。通过显示下载百分比、文件大小和下载速度,用户可以清楚地了解下载状态,减少等待过程中的焦虑感。

相关文章:

在Electron中实现实时下载进度显示的完整指南

在开发Electron应用时&#xff0c;提供良好的用户体验至关重要&#xff0c;尤其是在下载大文件时。用户需要知道下载进度、预计完成时间以及当前下载速度。本文将详细介绍如何在Electron应用中实现实时下载进度显示功能&#xff0c;从主进程到渲染进程的完整流程。 技术栈是ele…...

java生成一个可以下载的word文件

在 Java 里&#xff0c;你能够借助 Apache POI 库来生成 Word 文件&#xff0c;并且实现文件下载功能。下面为你详细介绍实现步骤和示例代码。 1. 添加依赖 若使用 Maven 项目&#xff0c;需在 pom.xml 里添加 Apache POI 的依赖&#xff1a; <dependencies><depen…...

MacBook部署达梦V8手记

背景 使用Java SpringBootDM开发Web应用&#xff0c;框架有License&#xff0c;OSX加载dll失败&#xff0c;安装了Windows 11&#xff0c;只有一个C盘&#xff0c;达梦安装后因为C盘权限问题&#xff0c;创建数据库失败&#xff0c;遂采用Docker容器方式部署。 下载介质 官网在…...

外贸 B2B 平台没落?多语言批发系统正在崛起

近年来&#xff0c;全球外贸行业正在发生快速变化&#xff0c;传统的 B2B 平台正面临越来越多的挑战&#xff0c;尤其是在面对新兴的多语言批发系统时。这种变化不仅影响了供应商和买家之间的交易方式&#xff0c;也正在推动外贸行业的数字化升级和转型。今天&#xff0c;让我们…...

[spring] Spring JPA - Hibernate 多表联查 1

[spring] Spring JPA - Hibernate 多表联查 之前在 [spring] spring jpa - hibernate 名词解释&配置 和 [spring] spring jpa - hibernate CRUD 简单的学习了一下怎么使用 Hibernate 实现 CRUD 操作&#xff0c;不过涉及到的部分都是逻辑上比较简单的实现——只在一张表上…...

鸿蒙Next开发实战教程—电影app

最近忙忙活活写了不少教程&#xff0c;但是总感觉千篇一律&#xff0c;没什么意思&#xff0c;大家如果有感兴趣的项目可以私信给幽蓝君写一写。 今天分享一个电影App。 这个项目也比较简单&#xff0c;主要是一些简单页面的开发和本地视频的播放以及横竖屏切换。 页面搭建以…...

共享栈 线程局部存储 线程互斥 线程同步 消费者生产者模型

共享栈 第一个主线程会在栈区 而当其他线程创建时实在共享区动态申请的栈区 线程局部存储 __thread 关键字 与编译有关 全局变量是被线程共享的 每个线程都能看到 修改 但是如果对该全局变量加上__thread关键字后 该全局变量就不会被共享 将变量在库中的每一个线程的属…...

停车场停车位数据集,标注停车位上是否有车,平均正确识别率99.5%,支持yolov5-11, coco json,darknet,xml格式标注

停车场停车位数据集&#xff0c;标注停车位上是否有车&#xff0c;平均正确识别率98.0&#xff05;&#xff0c;支持yolov5-11&#xff0c; coco json&#xff0c;darknet&#xff0c;xml格式标注 数据集-识别停车场所有车辆的数据集 数据集分割 一共184张图片 训练组 89&am…...

【Go】运算符笔记

基本数学运算 Go 语言支持常见的 算术运算符&#xff0c;用于执行数学计算。 运算符说明加法-减法*乘法/除法%取余自增--自减 整数运算只能得到整数部分 package mainimport ("fmt""math" )func main() {go_math() }func go_math() {x, y : 8, 5fmt.Pr…...

ssm框架之mybatis框架讲解

1&#xff0c;Mybatis 1.1 Mybatis概述 1.1.1 Mybatis概念 MyBatis 是一款优秀的持久层框架&#xff0c;用于简化 JDBC 开发 MyBatis 本是 Apache 的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code&#xff0c;并且改名为MyBatis 。2…...

CEF 多进程模式时,注入函数,获得交互信息

CEF 控制台添加一函数,枚举 注册的供前端使用的CPP交互函数有哪些-CSDN博客 上篇文章,是在模拟环境,单进程中设置的,这篇文章,将其改到正常多进程环境中设置。 对应于工程中的 CEF_RENDER项目 一、多进程模式中,改写 修改步骤 1、注入函数 client_app_render.cpp 在…...

Androidstudio出现警告warning:意外的元素

这些警告信息通常与 Android SDK 或系统镜像的配置文件有关&#xff0c;可能是由于 SDK 工具或系统镜像的版本不兼容或配置文件格式发生了变化。以下是解决这些警告的步骤&#xff1a; 1. 更新 Android SDK 工具 确保你使用的是最新版本的 Android SDK 工具&#xff1a; 打开…...

深入了解Linux —— git三板斧

版本控制器git 为了我们方便管理不同版本的文件&#xff0c;就有了版本控制器&#xff1b; 所谓的版本控制器&#xff0c;就是能够了解到一个文件的历史记录&#xff08;修改记录&#xff09;&#xff1b;简单来说就是记录每一次的改动和版本迭代的一个管理系统&#xff0c;同…...

Vala编程语言教程-运算符

运算符 ‌ 赋值操作。左操作数必须为标识符&#xff0c;右操作数必须为适当的值或引用。 ‌, -, /, *, %‌ 基础算术运算&#xff0c;作用于左右操作数。 运算符也可用于字符串拼接。 ‌, -, /, *, %‌ 左右操作数间算术运算&#xff0c;左操作数必须为标识符&#xff0c;运…...

C#本地将labelme数据集转换为机器视觉yolo数据集格式

C#本地&#xff0c;将labelme数据集转换为机器视觉yolo数据集格式 using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Text.Encodings.Web; using System.Text.RegularExpressions; using System.Text.U…...

【软件系统架构】单体架构

一、引言 在软件开发的漫长历程中&#xff0c;架构的选择一直是至关重要的决策。单体架构作为一种经典的架构模式&#xff0c;曾经在许多项目中发挥着不可替代的作用。虽然如今微服务等架构逐渐流行&#xff0c;但理解单体架构对于深入掌握软件架构体系仍然有着重要意义。 二、…...

【求助】【建议放弃】【谷粒商城版】Kubernetes

本文作者&#xff1a; slience_me 文章目录 Kubernetes【谷粒商城版】【建议放弃】1. docker安装2. kubernetes安装前3. kubeadm,kubelet,kubectl3.1 简介kubeadmkubeletkubectl常用指令 3.2 安装3.3 kubeadm初始化3.4 加入从节点(工作节点)3.5 安装Pod网络插件&#xff08;CNI…...

uniapp 实现微信小程序电影选座功能

拖动代码 /*** 获取点击或触摸事件对应的座位位置* 通过事件对象获取座位的行列信息* param {Event|TouchEvent} event - 点击或触摸事件对象* returns {Object} 返回座位位置对象&#xff0c;包含行(row)和列(col)信息&#xff0c;若未找到有效位置则返回 {row: -1, col: -1}*…...

python+flask实现360全景图和stl等多种格式模型浏览

1. 安装依赖 pip install flask 2. 创建Flask应用 创建一个基本的Flask应用&#xff0c;并设置路由来处理不同的文件类型。 from flask import Flask, render_template, send_from_directory app Flask(__name__) # 设置静态文件路径 app.static_folder static app.r…...

IntelliJ 配置文件plugin.xml

在 IntelliJ IDEA 插件开发中&#xff0c;plugin.xml 是插件的配置文件&#xff0c;它包含了关于插件的所有基本信息、扩展点、依赖关系等。该文件使用 XML 格式进行定义。以下是 plugin.xml 中常见的元素及其用途&#xff1a; <idea-plugin><!-- 插件的基本信息 --&…...

C# Unity 唐老狮 No.10 模拟面试题

本文章不作任何商业用途 仅作学习与交流 安利唐老狮与其他老师合作的网站,内有大量免费资源和优质付费资源,我入门就是看唐老师的课程 打好坚实的基础非常非常重要: Unity课程 - 游习堂 - 唐老狮创立的游戏开发在线学习平台 - Powered By EduSoho C# 1. 内存中&#xff0c;堆和…...

数据库系统——规范化1NF~BCNF

数据库规范化完全指南&#xff1a;从零到BCNF&#xff0c;中学生也能秒懂&#xff01;&#x1f4da;✨ 一、什么是数据库规范化&#xff1f; 科学定义 &#x1f50d; 数据库规范化是通过一系列规则&#xff08;范式&#xff09;将数据库表结构分解为更小、更高效、无冗余的表…...

第十五届蓝桥杯2024JavaB组省赛试题A:报数游戏

简单的找规律题目。题目给得数列&#xff0c;第奇数项是20的倍数&#xff0c;第偶数项时24的倍数。题目要求第n 202420242024 项是多少。这一项是偶数&#xff0c;所以答案一定是24的倍数&#xff0c;并且偶数项的个数和奇数项的个数各占一半&#xff0c;所以最终的答案ans( n…...

Matlab 汽车二自由度转弯模型

1、内容简介 Matlab 187-汽车二自由度转弯模型 可以交流、咨询、答疑 2、内容说明 略 摘 要 本文前一部分提出了侧偏角和横摆角速度作为参数。描述了车辆运动的运动状态&#xff0c;其中文中使用的参考模型是二自由度汽车模型。汽车速度被认为是建立基于H.B.Pacejka的轮胎模…...

关于 2>/dev/null 的作用以及机理

每个进程都有三个标准文件描述符&#xff1a;stdin&#xff08;标准输入&#xff09;、stdout&#xff08;标准输出&#xff09;和stderr&#xff08;标准错误&#xff09;。默认情况下&#xff0c;stderr会输出到终端。使用2>可以将stderr重定向到其他地方&#xff0c;比如…...

学c++的人可以几天速通python?

学了俩天啊&#xff0c;文章写纸上了 还是蛮有趣的...

HTML,CSS,JavaScript

HTML:负责网页的结构(页面元素和内容)。 CSS:负责网页的表现(页面元素的外观、位置等页面样式&#xff0c;如:颜色、大小等)。 Javascript:负责网页的行为(交互效果)。 MDN前端开发文档(MDN Web Docs) HTML HTML(HyperText Markup Language):超文本标记语言超文本:超越了文本的…...

微信小程序面试内容整理-图片优化

在微信小程序中,图片优化是提升加载速度、节省网络带宽和提高用户体验的重要步骤。图片通常是小程序页面中的主要资源,合理的图片优化能显著提高小程序的性能,尤其是在用户网络状况较差的情况下。 1. 选择合适的图片格式 不同的图片格式有不同的特点,选择合适的格式能够有效…...

Rocky Linux 9.x 基于 kubeadm部署k8s 1.32

一、部署说明 1、主机操作系统说明 序号操作系统及版本备注1Rocky Linux release 9下载链接&#xff1a;https://mirrors.163.com/rocky/9.5/isos/x86_64/Rocky-9.5-x86_64-minimal.iso 2、主机硬件配置说明 作用IP地址操作系统配置关键组件k8s-master01192.168.234.51Rocky…...

【每日学点HarmonyOS Next知识】上下拉列表、停止无限循环动画、页面列表跟随列表滑动、otf字体、日期选择

1、HarmonyOS 实现只需要保留上拉加载更多&#xff0c;但是不需要下拉刷新&#xff1f; Refresh通过参数refreshing判断当前组件是否正在刷新&#xff0c;可以控制该参数变化来触发下拉刷新&#xff1a;https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5…...