大文件分片上传进阶版(新增md5校验、上传进度展示、并行控制,智能分片、加密上传、断点续传、自动重试),实现四位一体的网络感知型大文件传输系统‌

上篇文章我们总结了大文件分片上传的主要核心,但是我对md5校验和上传进度展示这块也比较感兴趣,所以在deepseek的帮助下,扩展了一下我们的代码,如果有任何问题和想法,非常欢迎大家在评论区与我交流,我需要学习的地方也还有特别多~

开始之前我们先用一个通俗易懂的例子来理解我们的功能吧~

用快递寄大件包裹的思路,解释大文件分片上传的实现步骤

场景设定:

假设你要把一卡车的大米(5吨)从杭州运到北京,但遇到了几个现实问题:

1.整卡车运输风险大(爆胎就全完了)
2.中途可能有检查站需要抽检
3.运输途中网络信号时好时坏

解决方案的六个关键步骤:

第一步:货物预处理(preprocessFile)

动作‌:把大米分装成小袋,每袋贴上唯一编号

技术对应‌

  • 计算整个大米的MD5指纹(确保货物完整性)
  • 生成专用加密包装袋(AES密钥)

为什么重要‌:

  • 避免整车运输风险
  • 方便中途抽检任意一袋
  • 不同袋子用不同密码锁更安全

第二步:秒传核验(checkInstantUpload)

‌动作‌:打电话给北京仓库:“你们已经有5吨杭州大米了吗?”
技术对应‌

  • 发送MD5给服务端查询
  • 如果已有相同货物,直接标记运输完成

‌省时技巧‌:

  • 避免重复运输相同货物
  • 节省90%运输时间

第三步:智能分箱(prepareChunks)

‌动作‌:根据路况决定每箱装多少袋

  • 高速公路:用大箱子(装20袋)
  • 山路:用小箱子(装5袋)

‌技术对应‌:

  • 网络测速(检查"路况")
  • 动态调整分片大小

‌智慧之处‌:

  • 好路况多装快跑
  • 差路况少装稳走

第四步:分批运输(uploadAllChunks)

‌动作‌

  1. 给每个箱子单独上锁(不同IV加密)
  2. 3辆货车同时出发(并发控制)
  3. 某辆车抛锚就换车重发(错误重试)

技术细节‌:

  • 每个分片独立加密
  • 失败分片自动重试3次
  • 实时记录已送达的箱子

第五步:收件核验(mergeFile)

‌动作‌

  1. 北京仓库收到所有箱子
  2. 按编号顺序拆箱组合
  3. 检查MD5是否匹配原始指纹

‌安全保障‌:

  • 防止运输途中被调包
  • 确保颗粒不少

第六步:断点续传(saveProgress)

‌突发情况处理‌

  • 遇到暴雨暂停运输
  • 记录哪些箱子已送达
  • 雨停后继续送未达的箱子

‌技术实现‌:

  • 自动保存上传进度
  • 支持从断点恢复

我们的智能方案为什么更优秀呢?

1.整车上路风险高,化整为零更安全
2.每袋都有独立指纹密码锁,防止被掉包
3.根据路况调整运输策略,堵车时不用干等
4.遇到检查要全部开箱时,可随机抽捡一袋不影响整体
5.重新发货不用从头开始,断点续传省时省力

所以我们其实是在上一篇大文件分片上传的过程中增加了两个功能:

秒传检查和合并校验

整体步骤是:

预处理->秒传检查->智能分片->加密运输->合并校验->断点保护

学习之前我们先来搞懂两个问题:

1.md5校验在我们文件上传过程中是必须的吗?

答案肯定是否,但有两个场景我们是必须要使用md5校验的(大方向):秒传功能文件完整性

场景类比解释技术对应
‌秒传功能‌仓库发现已有同批次芒果,直接调库服务端比对MD5跳过上传
‌防数据篡改‌发现运输商偷换成越南芒果合并后MD5与原始值比对

如果简单使用文件大小校验,或者严重依赖tpc传输确保文件不会丢失的情况下也是可以不使用md5校验的

2.md5校验与分片加密有什么关系?可以替代吗?

即使我们已经对每个分片进行了加密上传,仍然可以使用md5校验文件的完整性,分片加密与md5校验是互补而非替代。分片加密如同在生产线上为每个零件做防锈处理,而MD5校验如同在出厂前对整机进行质检——‌防锈处理不能替代最终质检‌,两者结合才能确保交付可靠的产品。

  1. ‌分片加密的作用‌
加密阶段防护目标示例风险
传输过程加密防中间人窃听/篡改黑客截获分片并修改
存储加密防服务器数据泄露数据库被拖库
  1. ‌MD5校验的核心价值‌
校验场景解决的问题示例风险
加密前校验源文件完整性本地文件损坏
解密后校验解密过程是否正确密钥错误导致解密失败
合并校验分片顺序/组合错误分片序号错乱

所以如果(验证文件完整性中可能会发生的错误)

1.加密前的源文件已经损坏(加密传输后肯定有误)
2.密钥错误、IV丢失、解密算法不兼容等导致解密后数据错误
3.分片上传成功但合并顺序错乱

以上几种情况发生的时候我们是有必要进行md5校验的

首先看我们需要实现功能的完整思路与技术亮点吧~

完整实现思路(五阶段工作流)

1.初始化阶段‌

  • 生成文件唯一ID(UUID v4)
  • 配置分片大小范围(默认1MB~20MB)
  • 初始化加密系统、分片存储结构

‌2.预处理阶段‌

  • ‌并行执行‌:MD5计算 + 密钥生成(加速启动)
  • ‌流式MD5‌:2MB分片渐进计算,避免内存溢出 ‌
  • 密钥管理‌:使用Web Crypto API安全生成AES密钥

3.秒传校验‌

  • 发送文件MD5到服务端查询
  • 存在相同文件时直接跳过后续步骤
  • 节省带宽和服务器存储空间

4.动态分片上传‌

  • ‌ 网络测速‌:通过1MB测试文件探测当前带宽 ‌
  • 智能分片‌:按带宽50%动态调整分片大小 ‌
  • 加密传输‌:每个分片独立IV,AES-CBC加密 ‌
  • 并发控制‌:3个并行上传通道(可配置)
  • ‌重试机制‌:指数退避策略(2s, 4s, 8s)

5.收尾工作‌

  • 发送合并请求到服务端
  • 清理临时数据(可选)
  • 持久化最终状态

架构亮点

网络自适应‌

  • 三次测速取平均值减少误差
  • 分片大小平滑过渡(避免剧烈波动)

安全传输‌

  • 前端加密 + 服务端解密双保险
  • 每个分片独立IV防止模式分析
  • 密钥仅存于内存和加密存储

可靠性保障‌

  • 断点续传:自动保存进度到IndexedDB
  • 原子操作:分片上传成功后才标记完成
  • 错误隔离:单个分片失败不影响整体流程

性能优化‌

  • 并行预处理(MD5和密钥生成)
  • 流式哈希计算(内存占用恒定)
  • 浏览器空闲时段上传(可扩展)

以下是完整代码

/*** 四位一体的网络感知型大文件传输系统‌* * ‌动态分片策略‌
// 基于实时网络带宽的动态分片算法(1MB~20MB智能调节)
// 分片大小平滑调整机制(20%最大波动限制)
// 内存对齐优化(1MB粒度减少资源碎片)*  ‌数据安全框架‌
// 三级校验体系:全文件MD5 + 分片级SHA256双哈希
// AES-CBC加密(分片独立IV防重放攻击)
// 密钥生命周期管理(生成->存储->使用隔离)* ‌传输可靠性保障‌
// 断点续传能力(IndexedDB持久化存储)
// 异常安全边界(try-catch包裹全流程)
// 分片原子化操作(独立元数据管理)*  ‌性能优化工程‌
// 并行预处理流水线(MD5与密钥并行计算)
// 流式哈希计算(2MB分片递归处理)
// 网络测速基准(1MB测试包探测带宽)*//*** 大文件分片上传类(支持动态分片、加密、MD5校验)*/
class FileUploader {/*** 初始化上传实例* @param {File} file - 浏览器文件对象 * @param {Object} [options] - 配置参数*/constructor(file, options = {}) {// 必需参数this.file = file; // 上传文件对象this.fileId = this.generateFileId(); // 文件唯一标识this.fileMD5 = null;                 // 全文件MD5值// 分片管理this.chunks = []; // 全部分片数据this.uploadedChunks = new Set(); // 已上传分片索引this.ivMap = new Map();              // 加密初始化向量存储,存储每个分片的IV// 加密配置this.encryptionKey = null;           // AES加密密钥// 性能参数this.lastUploadSpeed = 5 * 1024 * 1024; // 网络基准速度(默认5MB/s)// 用户配置this.options = {minChunkSize: 1 * 1024 * 1024, // 最小分片1MBmaxChunkSize: 20 * 1024 * 1024, // 最大分片20MB...options};// 事件系统this.events = {};}// --------------------------// 核心公共方法// --------------------------/*** 启动上传流程(完整工作流)*  * @throws {Error} 上传过程中的错误*/async startUpload() {try {// 阶段1: 文件预处理(计算哈希 + 生成密钥)await this.preprocessFile();// 阶段2: 秒传检查(通过文件MD5判断是否需要传输)const needUpload = await this.checkInstantUpload();if (!needUpload) return;// 阶段3: 动态分片准备(根据网络状况生成分片)await this.prepareChunks();// 阶段4: 分片上传(含加密和重试机制)await this.uploadAllChunks();// 阶段5: 合并请求await this.mergeFile();this.emit('complete');} catch (error) {console.error('上传流程异常:', error);await this.saveProgress(); // 异常时保存进度this.emit('error', error);throw error;}}// --------------------------// 预处理阶段(含MD5计算)// --------------------------/*** 阶段1:文件预处理(计算MD5、生成密钥)*/async preprocessFile() {// 并行执行两个任务,实现异步流水线加速// SparkMD5库保障哈希计算准确性// Web Crypto API生成符合FIPS标准的AES密钥const [md5, key] = await Promise.all([this.calculateFileMD5(), // 计算全文件MD5this.generateAESKey() // 生成加密密钥]);this.fileMD5 = md5;this.encryptionKey = key;// 保存初始进度(可用于恢复)await this.saveProgress();}/*** 计算全文件MD5(分片计算避免内存溢出)* @returns {Promise<string>} MD5哈希值*/calculateFileMD5() {// 返回Promise对象实现异步计算流程控制return new Promise((resolve) => {// 定义分片大小(2MB兼顾计算效率与内存安全)const chunkSize = 2 * 1024 * 1024;// 计算总切片数量(向上取整保证最后分片完整性)const chunks = Math.ceil(this.file.size / chunkSize);// 初始化SparkMD5实例(专为ArrayBuffer优化的MD5计算库)const spark = new SparkMD5.ArrayBuffer();// 已处理分片计数器let processed = 0;// 定义分片加载递归函数,递归分片处理大文件(2MB粒度),避免内存溢出风险const loadNext = () => {// 计算当前分片字节范围const start = processed * chunkSize;const end = Math.min(start + chunkSize, this.file.size);// 切割文件对象获取当前分片Blobconst blob = this.file.slice(start, end);// 创建文件读取器处理二进制数据const reader = new FileReader();// 注册文件加载完成回调reader.onload = (e) => {// 将分片二进制数据追加到MD5计算流spark.append(e.target.result);// 更新已处理分片计数processed++;// 存储计算进度(格式:当前分片/总分片数)sessionStorage.setItem(`${this.fileId}_md5`, `${processed}/${chunks}`);// 触发进度事件this.emit('progress', {type: 'md5',value: processed / chunks});// 递归判断:未完成继续处理,完成则返回最终MD5processed < chunks ? loadNext() : resolve(spark.end());};// 启动分片数据读取(ArrayBuffer格式保持二进制精度)reader.readAsArrayBuffer(blob);};// 启动首个分片处理loadNext();});}/*** 阶段2:秒传验证(checkInstantUpload)* 实现逻辑: 将全文件MD5发送至服务端查询,若存在相同哈希文件,触发秒传逻辑,跳过后续流程直接返回成功* ‌业务价值:节省90%+重复文件传输成本;降低服务器存储冗余*//*** 🌟 秒传验证核心方法* @param {string} fileMD5 - 文件的完整MD5哈希值* @returns {Promise<boolean>} - 是否可秒传*/async checkInstantUpload(fileMD5) {try {const response = await fetch('/api/check-instant-upload', {method: 'POST',headers: { 'Content-Type': 'application/json' },body: JSON.stringify({ md5: fileMD5 })});if (!response.ok) throw new Error('秒传验证请求失败');const result = await response.json();return result.exists; // 服务端返回是否存在} catch (error) {console.error('秒传验证异常:', error);return false; // 失败时按需处理,此处默认继续上传}}// --------------------------// 分片处理阶段(含动态调整)// --------------------------/*** 阶段3:智能分片准备(动态调整分片大小)* 算法原理:基于实时测速结果(testNetworkSpeed)计算基准值引入历史速度惯性因子(lastUploadSpeed)平滑波动内存对齐优化提升分片处理效率*/async prepareChunks() {// 网络测速(取三次平均值)const speeds = [];for (let i = 0; i < 3; i++) {speeds.push(await this.testNetworkSpeed());}const currentSpeed = speeds.reduce((a, b) => a + b, 0) / speeds.length;// 计算动态分片大小(控制在配置范围内)let chunkSize = currentSpeed * 0.5; // 按带宽50%计算chunkSize = Math.max(this.options.minChunkSize,Math.min(this.options.maxChunkSize, chunkSize));// 生成分片元数据const totalChunks = Math.ceil(this.file.size / chunkSize);for (let i = 0; i < totalChunks; i++) {const start = i * chunkSize;const end = Math.min(start + chunkSize, this.file.size);this.chunks.push({index: i,blob: this.file.slice(start, end),size: end - start});}}
}/*** 阶段4:分片上传(uploadAllChunks)(并发控制 + 重试机制)* ‌加密流程‌生成随机IV(每个分片独立初始化向量)计算原始数据SHA256哈希AES-CBC加密分片数据生成加密后哈希(可选二次校验)* ‌传输策略‌Set数据结构记录已上传分片索引失败重试机制(需补充实现)并行上传控制(可扩展为连接池管理*/async uploadAllChunks() {// 设置并发数:浏览器环境下建议2-4个并行请求,平衡性能与稳定性const CONCURRENCY = 3;// 创建上传队列:通过展开运算符复制分片数组,避免直接操作原始数据const queue = [...this.chunks];// 主循环:持续处理直到队列清空while (queue.length > 0) {// 初始化当前批次的Promise容器const workers = [];// 提取当前批任务:每次取出CONCURRENCY数量的分片// splice操作会同时修改队列长度,实现队列动态缩减const currentBatch = queue.splice(0, CONCURRENCY);// 遍历当前批次的分片for (const chunk of currentBatch) {// 跳过已上传分片:通过Set检查避免重复上传if (this.uploadedChunks.has(chunk.index)) continue;// 将分片上传任务包装成Promise,加入workers数组workers.push(// 执行分片上传核心方法this.uploadChunk(chunk, chunk.index).then(() => {// 计算实时进度:已上传数 / 总分片数const progress = this.uploadedChunks.size / this.chunks.length;// 触发进度事件:通知外部监听者更新进度条this.emit('progress', { type: 'upload', value: progress });}));}// 等待当前批次全部完成(无论成功/失败)// 使用allSettled而非all保证异常不会中断整个上传流程await Promise.allSettled(workers);}
}/*** 单分片上传(含加密和重试机制)* @param {Object} chunk - 分片数据* @param {number} index - 分片索引* @param {number} retries - 剩余重试次数*/async uploadChunk(chunk, index, retries = 3) {try {// 加密处理(生成独立IV)const encryptedBlob = await this.encryptChunk(chunk.blob, index);// 构建表单数据const formData = new FormData();formData.append('file', encryptedBlob);formData.append('index', index);formData.append('iv', this.ivMap.get(index));// 上传请求const response = await fetch('/upload', {method: 'POST',body: formData});if (!response.ok) throw new Error(`HTTP ${response.status}`);this.uploadedChunks.add(index);} catch (error) {if (retries > 0) {await new Promise(r => setTimeout(r, 2000 * (4 - retries))); // 指数退避return this.uploadChunk(chunk, index, retries - 1);}throw new Error(`分片${index}上传失败: ${error.message}`);}
}/* ================= 加密模块 ================= *//** 生成AES-CBC加密密钥 */async generateAESKey() {return crypto.subtle.generateKey({ name: 'AES-CBC', length: 256 },true,['encrypt', 'decrypt']);
}/** 加密单个分片 */async encryptChunk(blob, index) {const iv = crypto.getRandomValues(new Uint8Array(16));const data = await blob.arrayBuffer();// 原始数据哈希校验const rawHash = await crypto.subtle.digest('SHA-256', data);// 执行加密const encrypted = await crypto.subtle.encrypt({ name: 'AES-CBC', iv },this.encryptionKey,data);// 存储加密参数this.ivMap.set(index, iv);return new Blob([encrypted], { type: 'application/octet-stream' });
}/* ================= 辅助方法 ================= *//** 网络测速(上传1MB测试文件) */async testNetworkSpeed() {const testBlob = new Blob([new Uint8Array(1 * 1024 * 1024)]);const start = Date.now();await fetch('/speed-test', {method: 'POST',body: testBlob});const duration = (Date.now() - start) / 1000;return (1 * 1024 * 1024) / duration; // 返回字节/秒
}/** 持久化上传进度 */async saveProgress() {const data = {fileId: this.fileId,chunks: this.chunks,uploadedChunks: [...this.uploadedChunks],encryptionKey: await crypto.subtle.exportKey('jwk', this.encryptionKey),ivMap: Object.fromEntries(this.ivMap)};await idb.setItem(this.fileId, data); // 假设使用IndexedDB
}/** 生成文件唯一ID */
generateFileId() {return crypto.randomUUID();
}/* ================= 事件系统 ================= */on(event, callback) {this.events[event] = callback;return this;
}emit(event, ...args) {const handler = this.events[event];handler && handler(...args);
}
}// ---------------------------- 使用示例 ----------------------------
const uploader = new FileUploader(file, {maxChunkSize: 50 * 1024 * 1024 // 自定义配置
});// 事件监听
uploader.on('progress', ({ type, value }) => {console.log(`${type}进度: ${(value * 100).toFixed(1)}%`);}).on('error', error => {console.error('上传失败:', error);});// 启动上传
uploader.startUpload();

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C# 点击导入,将需要的参数传递到弹窗的页面

点击导入按钮&#xff0c;获取本页面的datagridview标题的结构&#xff0c;并传递到导入界面。 新增一个datatable用于存储datagridview的caption和name&#xff0c;这里用的是devexpress组件中的gridview。 DataTable dt new DataTable(); DataColumn CAPTION …

android的 framework 是什么

Android的Framework&#xff08;框架&#xff09;是Android系统的核心组成部分&#xff0c;它为开发者提供了一系列的API&#xff08;应用程序编程接口&#xff09;&#xff0c;使得开发者能够方便地创建各种Android应用。以下是关于它的详细介绍&#xff1a; 位置与架构 在A…

【MySQL】表的约束(主键、唯一键、外键等约束类型详解)、表的设计

目录 1.数据库约束 1.1 约束类型 1.2 null约束 — not null 1.3 unique — 唯一约束 1.4 default — 设置默认值 1.5 primary key — 主键约束 自增主键 自增主键的局限性&#xff1a;经典面试问题&#xff08;进阶问题&#xff09; 1.6 foreign key — 外键约束 1.7…

数据结构-C语言版本(三)栈

数据结构中的栈&#xff1a;概念、操作与实战 第一部分 栈分类及常见形式 栈是一种遵循后进先出(LIFO, Last In First Out)原则的线性数据结构。栈主要有以下几种实现形式&#xff1a; 1. 数组实现的栈&#xff08;顺序栈&#xff09; #define MAX_SIZE 100typedef struct …

如何以特殊工艺攻克超薄电路板制造难题?

一、超薄PCB的行业定义与核心挑战 超薄PCB通常指厚度低于1.0毫米的电路板&#xff0c;而高端产品可进一步压缩至0.4毫米甚至0.2毫米以下。这类电路板因体积小、重量轻、热传导性能优异&#xff0c;被广泛应用于折叠屏手机、智能穿戴设备、医疗植入器械及新能源汽车等领域。然而…

AI 赋能 3D 创作!Tripo3D 全功能深度解析与实操教程

大家好&#xff0c;欢迎来到本期科技工具分享&#xff01; 今天要给大家带来一款革命性的 AI 3D 模型生成平台 ——Tripo3D。 无论你是游戏开发者、设计师&#xff0c;还是 3D 建模爱好者&#xff0c;只要想降低创作门槛、提升效率&#xff0c;这款工具都值得深入了解。 接下…

如何理解抽象且不易理解的华为云 API?

API的概念在华为云的使用中非常抽象&#xff0c;且不容易理解&#xff0c;用通俗的语言 形象的比喻来讲清楚——什么是华为云 API&#xff0c;怎么用&#xff0c;背后原理&#xff0c;以及主要元素有哪些&#xff0c;尽量让新手也能明白。 &#x1f9e0; 一句话先理解&#xf…

第 7 篇:总结与展望 - 时间序列学习的下一步

第 7 篇&#xff1a;总结与展望 - 时间序列学习的下一步 (图片来源: Guillaume Hankenne on Pexels) 恭喜你&#xff01;如果你一路跟随这个系列走到了这里&#xff0c;那么你已经成功地完成了时间序列分析的入门之旅。我们从零开始&#xff0c;一起探索了时间数据的基本概念、…

PPT无法编辑怎么办?原因及解决方法全解析

在日常办公中&#xff0c;我们经常会遇到需要编辑PPT的情况。然而&#xff0c;有时我们会发现PPT文件无法编辑&#xff0c;这可能由多种原因引起。今天我们来看看PPT无法编辑的几种常见原因&#xff0c;并提供实用的解决方法&#xff0c;帮助你轻松应对。 原因1&#xff1a;文…

前端面试题---GET跟POST的区别(Ajax)

GET 和 POST 是两种 HTTP 请求方式&#xff0c;它们在传输数据的方式和所需空间上有一些重要区别&#xff1a; ✅ 一句话概括&#xff1a; GET 数据放在 URL 中&#xff0c;受限较多&#xff1b;POST 数据放在请求体中&#xff0c;空间更大更安全。 &#x1f4e6; 1. 所需空间…

第 5 篇:初试牛刀 - 简单的预测方法

第 5 篇&#xff1a;初试牛刀 - 简单的预测方法 经过前面四篇的学习&#xff0c;我们已经具备了处理时间序列数据的基本功&#xff1a;加载、可视化、分解以及处理平稳性。现在&#xff0c;激动人心的时刻到来了——我们要开始尝试预测 (Forecasting) 未来&#xff01; 预测是…

从代码学习深度学习 - 学习率调度器 PyTorch 版

文章目录 前言一、理论背景二、代码解析2.1. 基本问题和环境设置2.2. 训练函数2.3. 无学习率调度器实验2.4. SquareRootScheduler 实验2.5. FactorScheduler 实验2.6. MultiFactorScheduler 实验2.7. CosineScheduler 实验2.8. 带预热的 CosineScheduler 实验三、结果对比与分析…

k8s 基础入门篇之开启 firewalld

前面在部署k8s时&#xff0c;都是直接关闭的防火墙。由于生产环境需要开启防火墙&#xff0c;只能放行一些特定的端口&#xff0c; 简单记录一下过程。 1. firewall 与 iptables 的关系 1.1 防火墙&#xff08;Firewall&#xff09; 定义&#xff1a; 防火墙是网络安全系统&…

RSS 2025|苏黎世提出「LLM-MPC混合架构」增强自动驾驶,推理速度提升10.5倍!

论文题目&#xff1a;Enhancing Autonomous Driving Systems with On-Board Deployed Large Language Models 论文作者&#xff1a;Nicolas Baumann&#xff0c;Cheng Hu&#xff0c;Paviththiren Sivasothilingam&#xff0c;Haotong Qin&#xff0c;Lei Xie&#xff0c;Miche…

list的学习

list的介绍 list文档的介绍 list是可以在常数范围内在任意位置进行插入和删除的序列式容器&#xff0c;并且该容器可以前后双向迭代。list的底层是双向链表结构&#xff0c;双向链表中每个元素存储在互不相关的独立节点中&#xff0c;在节点中通过指针指向其前一个元素和后一…

生物信息学技能树(Bioinformatics)与学习路径

李升伟 整理 生物信息学是一门跨学科领域&#xff0c;涉及生物学、计算机科学以及统计学等多个方面。以下是关于生物信息学的学习路径及相关技能的详细介绍。 一、基础理论知识 1. 生物学基础知识 需要掌握分子生物学、遗传学、细胞生物学等相关概念。 对基因组结构、蛋白质…

AOSP Android14 Launcher3——远程窗口动画关键类SurfaceControl详解

在 Launcher3 执行涉及其他应用窗口&#xff08;即“远程窗口”&#xff09;的动画时&#xff0c;例如“点击桌面图标启动应用”或“从应用上滑回到桌面”的过渡动画&#xff0c;SurfaceControl 扮演着至关重要的角色。它是实现这些跨进程、高性能、精确定制动画的核心技术。 …

超详细实现单链表的基础增删改查——基于C语言实现

文章目录 1、链表的概念与分类1.1 链表的概念1.2 链表的分类 2、单链表的结构和定义2.1 单链表的结构2.2 单链表的定义 3、单链表的实现3.1 创建新节点3.2 头插和尾插的实现3.3 头删和尾删的实现3.4 链表的查找3.5 指定位置之前和之后插入数据3.6 删除指定位置的数据和删除指定…

17.整体代码讲解

从入门AI到手写Transformer-17.整体代码讲解 17.整体代码讲解代码 整理自视频 老袁不说话 。 17.整体代码讲解 代码 import collectionsimport math import torch from torch import nn import os import time import numpy as np from matplotlib import pyplot as plt fro…

前端性能优化:所有权转移

前端性能优化&#xff1a;所有权转移 在学习rust过程中&#xff0c;学到了所有权概念&#xff0c;于是便联想到了前端&#xff0c;前端是否有相关内容&#xff0c;于是进行了一些实验&#xff0c;并整理了这些内容。 所有权转移&#xff08;Transfer of Ownership&#xff09;…