news 2026/9/16 5:00:24

ASP.NET大文件上传实战:断点续传与AES加密完整方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ASP.NET大文件上传实战:断点续传与AES加密完整方案

在医院信息化干了这些年,碰上最让人头疼的需求之一,就是“大文件上传”。CT影像、病理切片、手术录像、远程会诊记录,动辄几百MB甚至几个GB,网络一抖动,传了半小时直接失败,患者那边等着报告,科室那边催着数据,压力全在工程师身上。更麻烦的是,医疗数据还有合规要求,患者隐私不能泄露,文件落盘不能是明文,等保测评一来,检察人员首先问的就是“上传过程数据有没有加密?存储有没有加密?密钥怎么管的?”

这篇文章就围绕“ASP.NET大文件上传”这个场景,把断点续传和加密这两块怎么落地,一次说透。内容是基于我实际做过的医疗集成平台项目整理出来的,会给出完整的方案设计、关键代码、参数取舍和踩坑记录,适合正在做医院信息化、医疗影像上传、远程医疗系统的.NET开发人员参考。

1. 先想清楚:医疗大文件上传到底难在哪

1.1 医疗数据的特殊性决定了方案不能照搬通用做法

普通系统里的大文件上传,无非是调个组件、改个超时时间,顶多再加个进度条。但医疗系统不一样,数据性质决定了技术选型必须足够谨慎。首先是文件体积,一张乳腺钼靶的无损影像可能300MB,一段4K手术录像能到2GB以上,这类文件用传统的multipart/form-data一次性POST,Web服务器默认请求体限制就是30秒超时、4MB大小,不改配置连小文件都传不上去。

其次是网络环境。医院的网络不是互联网公司那种IDC机房环境,不少基层医院内网带宽只有百兆,跨院区专线丢包率还高。我之前见过一个项目,影像科上传断层扫描数据到区域平台,网络一波动整包重传,骨科的医生直接在群里开骂。所以断点续传不是“锦上添花”,而是“没有这个功能系统根本不敢上线”。

再就是合规要求。医疗数据受法律和等级保护约束,患者影像属于隐私数据,如果明文落盘,一旦数据库或者存储被拖库,责任不是罚钱那么简单。这里就引申出两个硬性需求:传输通道要加密,存储文件要加密。两者缺一不可。

1.2 常规上传方案的瓶颈在哪里

很多人遇到大文件上传,第一反应是调大maxAllowedContentLengthmaxRequestLength,或者改IIS的requestFiltering,再不行就上UploadifyWebUploader这类现成组件。这么做的确能解决一部分问题,但有几个硬伤:

  • 整包上传没有断点能力:一旦网络中断、浏览器崩溃、服务器回收进程,整个文件就得重新传,用户的耐心和医院的带宽都耗不起。
  • 服务器内存压力大:大文件整体读入内存再写盘,并发一高,ASP.NET进程的内存直接飙升,IIS应用池重启,正在上传的文件全部中断。
  • 明文传输风险:默认HTTP通道下,文件在传输中可被中间人截获。医疗影像如果被篡改,影响的是诊断结论,这是医疗事故级别的风险。
  • 无完整性校验:文件传完也不知道有没有丢字节,影像数据坏了一个字节,阅片软件就可能打不开。

所以这个项目从一开始就定了思路:前端把文件切成多个分片,逐片上传,每片独立校验;后端接收后落盘到临时目录,全部完成后合并;传输走HTTPS,落盘用AES加密;另外再用状态记录表跟踪每个文件上传到哪里,随时可以续传。

1.3 方案选型的核心思路

技术栈选的是ASP.NET Core Web API + 前端原生JavaScript(配合Web Worker做分片计算)。之所以不选ASP.NET Framework的老方案,一是跨平台部署方便,医院机房linux上也能跑;二是ASP.NET Core内置了更灵活的中间件管道,处理分片上传和超大请求体更顺手;三是System.Security.Cryptography类库同时支持AES-GCM这些新算法,医疗场景下加密需求完全可以覆盖。

技术环节推荐方案理由
后端框架ASP.NET Core Web API跨平台、中间件灵活、性能好
前端分片JavaScript File API + Blob.slice原生支持,不需要额外依赖
分片计算Web Worker避免大文件读MD5时卡死UI线程
传输加密HTTPS/TLS 1.2+防窃听、防中间人篡改
存储加密AES-256-GCM性能好,带认证加密,防篡改
断点记录数据库存储上传状态支持多端续传、进度查询

2. 断点续传的完整落地:从分片到合并再到状态恢复

2.1 前端分片策略:先算再传

断点续传的第一步,是在浏览器端把大文件切成小片。前端核心逻辑很简单:用File对象的slice方法把文件按固定大小切开,然后逐片上传。但有一个细节必须提前处理好——计算整个文件的唯一标识(MD5或SHA256),否则服务端无法判断“这些分片属于哪个文件”。

医疗场景里,文件动不动1GB以上,对整个文件计算MD5可真不是个秒级操作。直接在UI线程里跑,浏览器会直接提示“页面无响应”。所以这里我用了Web Worker来做分片和哈希计算。

// worker.js self.onmessage = function(event) { const file = event.data.file; const chunkSize = event.data.chunkSize; // 建议 5MB 或 10MB const chunkCount = Math.ceil(file.size / chunkSize); for (let i = 0; i < chunkCount; i++) { const start = i * chunkSize; const end = Math.min(start + chunkSize, file.size); const chunk = file.slice(start, end); // 这里用 FileReader 读取分片内容,再算每一片的 md5 const reader = new FileReader(); reader.onload = function() { const md5 = SparkMD5.ArrayBuffer.hash(reader.result); self.postMessage({ index: i, md5 }); }; reader.readAsArrayBuffer(chunk); } };

前端主线程拿到每个分片的md5后,再逐片上传,服务端接收分片时也要校验分片md5,防止传输过程中数据损坏。

分片大小怎么选,我实测下来有个经验:局域网内建议5MB,公网/专网建议1~2MB。分片太小,请求次数暴增,网络握手开销大;分片太大,单片传输时间长,一旦失败重传成本高。医疗系统如果部署在院内,5MB基本是性价比最高的;如果是跨院区公网传输,建议2MB。

2.2 前端断点续传的恢复逻辑

断点续传不是“重新传一遍”,而是“跳过已经传好的分片”。前端需要做两件事:

  • 上传前查询服务端,看看该文件已接收到哪些分片;
  • 只上传缺失的分片。

查询接口设计成GET /api/upload/status?fileMd5=xxx,返回结构类似:

{ "fileMd5": "5d41402abc4b2a76b9719d911017c592", "uploadedChunks": [0, 1, 2, 5, 6], "totalChunks": 10 }

前端拿到uploadedChunks数组后,用Set过滤需要上传的分片索引,从缺失位置继续传。这里有个隐藏细节:如果第3片传了一半网络断了,服务端没有记录它完成,那么它就不在uploadedChunks里,下一次会重新传第3片,完全没问题。

另外,医院内网经常出现“同一个患者房间里网络差,换个房间Wi-Fi就断了”的混乱场景,所以我把断点信息放在后端数据库里存着,而不是放在浏览器本地存储。这样做的好处是:换电脑、换浏览器、甚至换了个人操作,只要知道文件的md5,都能继续传到一半的任务。

2.3 后端分片接收与临时文件管理

后端接收分片的接口,我设计了两个:一个是POST /api/upload/chunk,负责接收单个分片;一个是POST /api/upload/merge,负责合并所有分片。每个分片请求都带上文件的唯一标识、当前分片索引、总分片数、分片大小等元数据。

[HttpPost("chunk")] public async Task<IActionResult> UploadChunk([FromForm] ChunkUploadRequest request) { var file = request.File; // IFormFile var fileMd5 = request.FileMd5; // 整个文件的MD5 var chunkIndex = request.ChunkIndex; // 当前分片索引 var totalChunks = request.TotalChunks; // 建立临时目录:/uploads/temp/{fileMd5}/ var tempDir = Path.Combine(_env.ContentRootPath, "uploads", "temp", fileMd5); Directory.CreateDirectory(tempDir); var chunkPath = Path.Combine(tempDir, $"{chunkIndex}.part"); // 先算一下收到的分片MD5,与前端传来的md5对比 using var stream = file.OpenReadStream(); var sha256 = SHA256.Create(); var receivedHash = Convert.ToHexString(await sha256.ComputeHashAsync(stream)); stream.Position = 0; if (!receivedHash.Equals(request.ChunkHash, StringComparison.OrdinalIgnoreCase)) { return BadRequest(new { message = "分片校验失败,请重传" }); } await using (var fs = new FileStream(chunkPath, FileMode.Create)) { await file.CopyToAsync(fs); } // 记录已上传分片到数据库或Redis _uploadProgressService.MarkChunkUploaded(fileMd5, chunkIndex, totalChunks); return Ok(new { message = "chunk uploaded" }); }

这里有几个操作细节必须说清楚:

  • 分片文件命名用{index}.part,方便合并时按索引从小到大拼接,避免乱序问题。
  • 临时目录以文件MD5命名,不同文件互不干扰。
  • 每个分片写完就close,释放句柄,避免高并发时文件锁定冲突。
  • 校验分片哈希的服务端CPU开销不小,但医疗数据不能省,宁可慢一点,不能错一点。

2.4 合并分片环节的关键处理

所有分片传完后,前端请求合并接口。服务端要做三步:

  1. 检查是否所有分片都到达;
  2. 按索引顺序合并分片文件;
  3. 合并后再计算整个文件的SHA256,与前端传的整个文件哈希做比对。
[HttpPost("merge")] public async Task<IActionResult> MergeChunks([FromBody] MergeRequest request) { var tempDir = Path.Combine(_env.ContentRootPath, "uploads", "temp", request.FileMd5); if (!Directory.Exists(tempDir)) return NotFound("未找到上传记录"); // 检查分片完整性 var expectedChunks = request.TotalChunks; for (int i = 0; i < expectedChunks; i++) { var partPath = Path.Combine(tempDir, $"{i}.part"); if (!File.Exists(partPath)) return BadRequest($"缺少分片 {i}"); } var finalPath = Path.Combine(_env.ContentRootPath, "uploads", "raw", $"{request.FileMd5}_{DateTime.Now:yyyyMMddHHmmss}.dat"); await using (var finalStream = new FileStream(finalPath, FileMode.Create)) { for (int i = 0; i < expectedChunks; i++) { var partPath = Path.Combine(tempDir, $"{i}.part"); await using var partStream = new FileStream(partPath, FileMode.Open); await partStream.CopyToAsync(finalStream); } } // 校验完整文件哈希 await using var verifyStream = File.OpenRead(finalPath); var sha256 = SHA256.Create(); var fileHash = Convert.ToHexString(await sha256.ComputeHashAsync(verifyStream)); if (!fileHash.Equals(request.FileHash, StringComparison.OrdinalIgnoreCase)) { // 哈希不匹配,说明合并有问题,删掉重来 File.Delete(finalPath); return BadRequest("文件完整性校验失败"); } // 删除临时分片文件 Directory.Delete(tempDir, true); // 在这里触发加密落盘逻辑,见下文 await _encryptionService.EncryptFile(finalPath); return Ok(new { fileId = request.FileMd5, size = new FileInfo(finalPath).Length }); }

分片合并这一步,最大的坑是磁盘空间。医院环境里,多个科室同时上传大文件,临时目录会以惊人的速度膨胀。我建议运维上做两层保障:一是设置.NET后台定时任务,定期清理超过24小时未合并的临时目录;二是在合并前先检查磁盘剩余空间,低于预警值就拒绝新的分片上传请求,防止磁盘写满导致系统崩溃。

3. 加密功能:传输加密、存储加密、密钥管理三步走

3.1 传输层加密:HTTPS不是可选项

先说最基础但也是最重要的一层——传输加密。医疗系统的Web API和前端之间,必须用HTTPS协议。这一条没什么可商量的,在ASP.NET Core里配置HTTPS重定向中间件只用几行代码:

app.UseHttpsRedirection();

但要注意,仅仅是配置了HTTPS还不够,IIS或Nginx的TLS版本必须限制在1.2及以上,禁掉SSLv3、TLS 1.0这些老协议。不然等保测评人员拿扫描器一扫,直接给你贴个“启用了弱加密协议”的漏洞标签。医院这种环境,我更推荐网关层做HTTPS卸载,把证书管理统一放在Nginx/负载均衡器上,应用层专注处理业务逻辑。

还有个小细节:分片上传接口如果走CDN或者云存储直传,那云存储那边的访问链接也要强制HTTPS,而且签名链接必须设置有效期,不能给个永久公网链接让别人随便下载患者影像。

3.2 存储加密方案选型:AES-GCM优于AES-CBC

文件传到服务器后,如果直接落盘成明文,那前面做的加密传输意义就减半了。数据库可能被拖库,服务器磁盘可能被拆走,或者运维人员直接拷走原始文件,这些都是真实潜在风险点。

针对这类文件加密,我用的是AES-256-GCM。选GCM而不是大家更熟悉的CBC,原因有两点:

  • GCM是AEAD算法,加密的同时产生认证标签,能检测密文是否被篡改。医疗影像一旦被篡改,轻则误诊,重则出人命,完整性必须优先保证。
  • GCM可以并行计算,性能比CBC好,对动辄GB级别的影像文件来说,加密耗时直接关系到用户等待时间。

加密接口可以封装成一个服务,核心逻辑如下:

public async Task EncryptFile(string plainFilePath) { var key = _keyProvider.GetCurrentKey(); // 从密钥管理系统获取 var nonce = new byte[12]; // GCM 推荐 96-bit nonce RandomNumberGenerator.Fill(nonce); var plainName = Path.GetFileName(plainFilePath); var encryptedPath = Path.Combine(_env.ContentRootPath, "uploads", "encrypted", $"{plainName}.enc"); await using var plainStream = File.OpenRead(plainFilePath); await using var encryptedStream = File.Create(encryptedPath); // 先写文件元信息(加密文件名、nonce、密钥版本等),方便解密时还原 await encryptedStream.WriteAsync(nonce); await using var cryptoStream = new CryptoStream( encryptedStream, new AesGcmCryptoTransform(key, nonce), CryptoStreamMode.Write); await plainStream.CopyToAsync(cryptoStream); await cryptoStream.FlushFinalBlockAsync(); }

实际项目里.NET内置AesGcm类可以直接用,不需要自己写AesGcmCryptoTransform;上面的写法是为了示意封装。注意几点:

  • 每个文件加密时都要生成独立的随机nonce,绝对不能复用,否则相同的明文会得出相同的密文前缀,给攻击者提供分析素材。
  • 加密后的文件头要保存nonce和密钥版本号,解密时先读头部信息,再拿对应版本密钥解密。
  • 加密完成后删除明文临时文件,避免明文残留在磁盘上。

3.3 密钥管理:医疗行业最容易被忽略的一环

很多团队把加密做好了,但密钥就写死在Web.config或appsettings.json里,这就等于把保险柜钥匙贴在保险柜门上。医疗行业对加密密钥的管理,检查人员不只看算法,更看密钥的生命周期管理。

我在项目中采用的是“集中密钥管理 + 数据库存储密钥版本 + 定期轮换”的组合方案:

  • 密钥不落应用配置文件,而是放在独立的密钥管理服务里(可以是Azure Key Vault,也可以是自建的加密机/HSM,医院内网环境更多用后者)。
  • 加密时通过服务鉴权获取当前活动密钥,解密时根据文件头里的密钥版本号获取对应密钥。
  • 每年至少轮换一次主密钥,轮换后旧密钥仍然保留解密能力,但不再用于新文件的加密。

用表格总结一下密钥管理策略:

管理点做法目的
密钥存储独立密钥服务/HSM防止密钥随代码泄露
密钥版本密文头部写入版本号支持密钥轮换与历史解密
轮换周期每年至少一次降低单密钥长期泄露风险
审计记录密钥访问日志等保要求的可追溯性
备份密钥分片备份,多人保管防止密钥丢失导致数据永久不可解密

这里特别提醒一句:不确定的密钥千万别弄丢了。医疗数据要保存很长时间,有些病历资料法定保存期限是30年,你加密做得再牛,密钥丢了文件一样等于废了。我见过某个PACS项目,运维把加密机搞挂了,又没有做主备容灾和备份,几千份历史影像直接打不开,这个事故差点成法律纠纷。所以做加密方案之前,先把密钥备份和灾备方案考虑好,这不是小事。

3.4 透明加密和数据库加密的补充

除了文件本身,医疗系统还有大量结构化数据存在数据库里,比如患者姓名、身份证号、诊断结论。文件上传方案通常还关联一个上传记录表,表里包含患者ID、文件名、大小、操作人等字段。这些字段如果以明文存数据库,那数据库一旦被脱库,加密文件本身再强也没用——因为攻击者可以通过数据库里的关联关系定位敏感文件。

我的建议是:

  • 数据库里的敏感字段(患者ID、身份证、诊断信息)用对称加密存储,应用层负责加解密;
  • 文件存储路径本身要加密或至少用无规则GUID命名,不要暴露“患者姓名-文件名”这样的明文对应关系;
  • 如果医院要求更严格,可以考虑部署透明的数据库加密插件,但这类产品大多绑定数据库厂商,选型时要注意兼容性。

另外有朋友问到Linux环境下的透明加密。坦白说,那套方案更适合服务器本地文件系统统一加密场景,它会钩子到VFS层,对应用透明。但医疗系统一旦上云或者多节点共享存储,透明加密的密钥管理会变得很复杂,而且它防不了应用层漏洞导致的数据泄露。所以我个人不做首选,优先在应用层做好显式加密,这样可控性最强。

4. 常见问题与排查技巧实录

4.1 大文件上传老是超时,页面502

这种问题十有八九不是代码的问题,而是IIS和ASP.NET Core的请求体大小限制没放开。ASP.NET Core默认请求体限制是30MB,IIS的maxAllowedContentLength默认是30000000字节,约28.6MB。两层都得改:

<!-- web.config --> <system.webServer> <security> <requestFiltering> <requestLimits maxAllowedContentLength="4294967295" /> </requestFiltering> </security> </system.webServer>
// Program.cs builder.Services.Configure<FormOptions>(options => { options.MultipartBodyLengthLimit = 1099511627776; // 1TB,为分片预留足够余量 options.ValueLengthLimit = int.MaxValue; options.MultipartHeadersLengthLimit = int.MaxValue; });

同时Kestrel的MaxRequestBodySize也要设置为null(不限制),不然改完IIS还有Kestrel这一层卡着。

4.2 分片传完了,合并时发现缺少中间某一片

这种问题的出现原因比较隐蔽。很多前端框架的并发上传队列有错误重试机制,但如果分片请求超时后前端把请求“放弃”了,而服务端其实已经写盘成功,此时数据库记录还没更新,就会出现“服务端有文件但状态记录缺失”的情况。我排查了半夜后定位到的原因就是这个:前端并发数太高,部分慢分片被中断,前端重新生成新的分片ID,导致旧的分片变成孤儿文件。

解决办法有三条:合并前做全量分片检查,缺哪片就精确重传哪片;前端并发数控制在3~5个,不要开几十个并发;服务端记录分片状态时加幂等判断,同一个chunkIndex重复提交时直接覆盖旧文件并返回成功。

4.3 AES加密后文件变大,甚至解密失败

GCM加密会在密文后面追加16字节的认证标签,所以加密文件比明文多出28字节左右(nonce 12字节 + tag 16字节),这是正常现象。如果解密失败,最常见的原因是nonce没有正确保存或读取错位。我见过一个同事把nonce写到密文末尾,结果解密时先读了末尾数据当nonce用,怎么都解不开。

约定好格式很重要。我的建议是固定文件头结构:前4字节存nonce长度,接着存nonce,再存4字节的密钥版本号,然后才是真正的密文。加解密逻辑写在同一模块里,避免两端理解不一致。

4.4 加密性能太差,CPU飙高

服务器在做AES-GCM加密大文件时,CPU密集计算是无法避免的,但可以通过几种方式把开销降下来:

  • 加密操作放到异步后台任务执行,不要卡住API请求线程。前端只负责上传和发起“加密请求”,服务器返回“加密处理中”的状态,前端定时轮询加密结果。
  • 用更高性能的服务器或启用AES-NI指令集(现在绝大多数X86服务器都支持),.NET在支持AES-NI时,会自动利用硬件加速。
  • 文件合并完成后再整体加密,比边接收边加密性能更好,因为加密前的分片可以直接写入,减少反复加解密。

实际项目中,1GB的文件用AES-256-GCM加密,在主流服务器上也就2~3秒的事,几乎可以忽略。真正耗时的瓶颈反而是磁盘IO,所以给系统配SSD比纠结加密算法更有效。

4.5 兼容性坑:医院里还有一堆老浏览器和国产化客户端

医院信息化环境鱼龙混杂,有的科室电脑还是Windows 7 + IE11,有的装的是国产安全浏览器。前端分片上传用的是File APIBlob.slice,IE11对Blob.slice支持不好,有的版本还需要带前缀的msSlice。虽然现在大多数项目可以用现代浏览器,但做医疗系统还是要有一颗兼容的心。

如果实在要兼容老浏览器,我的建议是:

  • 对IE类浏览器降级成“整包上传 + 服务端断点续传”方案——也就是前端不切片,但服务端仍将收到的流式文件按块存储,断点时的最后一块通过重试补传;
  • 或者直接和医院沟通,要求阅片室浏览器升级到Chrome/Edge内核,很多时候只要信息科出面就能搞定。

4.6 文件损坏、MD5计算耗时太长怎么优化

前面用了Web Worker计算整个文件MD5,但1GB文件算MD5仍然要好几秒。如果想加速,可以并行计算每个分片的md5,然后用分片md5组成新字符串再算一次整体标识。这样既做到“每个分片快速校验”,又用分片摘要作为该文件的唯一标识。代码里可以这样:

// 每片算完先存到数组 chunkMd5s.push(md5); if (chunkMd5s.length === chunkCount) { const fileId = SparkMD5.hash(chunkMd5s.join('')); // 用 fileId 作为文件上传的唯一标识 }

这种做法虽然不能和“整文件MD5”严格等价,但对断点续传来说已经足够,因为分片清单本身就是对该文件内容的约束,不同文件产生相同分片摘要组合的概率可以忽略。

5. 上线前别忘了这些检查项

方案写完了,代码跑通了,上线前还要对照检查一遍。我通常按这样一个清单来验收:

  • 传输协议是不是全部强制HTTPS,HTTP请求是否被重定向或拒绝;
  • 上传临时目录是否有权限管控,是否禁止脚本执行;
  • 加密后的文件是否落到了独立存储区,明文文件是否彻底清理;
  • 密钥有没有备份,密钥服务有没有做高可用部署;
  • 数据库里的患者关联字段是否已加密;
  • 有没有上传审计日志,谁在什么时候上传了什么文件,记录可查询;
  • 断点续传是否能在模拟断网(拔网线、杀进程)后正常恢复;
  • 并发上传场景下,分片记录是否出现状态错乱。

这些项目全部通过,系统才敢拿到医院真实环境里跑。尤其是最后两项,我建议在执行大版本上线或等保测评演练前,拉一台测试机模拟弱网环境,真实地传一个1GB以上的文件看看恢复情况,别到患者真正要传报告的时候掉链子。

好,思路和代码都写在上面了。如果你也在做类似医疗大文件上传的项目,可以直接按这套思路搭骨架,再根据医院的实际网络条件、合规等级做微调。如果你在实施过程中碰到了别的问题,也欢迎评论区交流,我尽量把坑提前帮你踩了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/16 5:00:09

DeepSeek Harness升级0.1.5-rc插件兼容性排查与修复全指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/16 5:00:03

嵌入式以太网设计:独立PHY+带变压器RJ45座实用指南

做嵌入式这些年&#xff0c;但凡项目要联网&#xff0c;很多人第一反应是换一颗带MAC的MCU再外挂一颗PHY&#xff0c;或者干脆上Linux接USB网卡。实际在工业设备、仪器仪表、网关这类场景里&#xff0c;很多时候只需要最简单可靠的10/100M有线以太网&#xff0c;一颗独立的10/1…

作者头像 李华
网站建设 2026/9/16 4:59:58

NV-SRAM与RA8D2 MCU打造不掉电数据完整性方案

去年做工业数据采集终端时&#xff0c;客户问了一句“断电瞬间&#xff0c;正在跑的数据会不会丢”。这个需求以前处理时是真头疼&#xff1a;EEPROM怕写满寿命&#xff0c;Flash怕写一半掉电&#xff0c;最后翻遍了方案&#xff0c;发现一套组合特别省心——主控用瑞萨RA8D2系…

作者头像 李华
网站建设 2026/9/16 4:59:58

手把手拆解SiamFC孪生网络跟踪器

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/16 4:59:52

Android车载串口开发实战:UART/RS232/RS485与权限排障全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/16 4:58:59

WordPress积分阅读插件对比:3种方案实测,选错多花多少钱

WordPress积分阅读插件对比:3种方案实测,选错多花多少钱 改个需求建站公司拖一周,最后还要加钱?这种憋屈感,做过网站的都懂。特别是像“积分阅读”这种小功能,大公司嫌小不接,小公司接了又改得面目全非。这时候你心里肯定在打鼓:到底自己弄要 多少钱 ?是买现成插件省心,还是找人定制靠谱?…

作者头像 李华