干了这么多年实验数据管理,我最大的感受就是视频回传这事儿看着简单,实际坑多。实验现场录了十几个G的高清视频,要跨网段传回内网平台,网络抖一下、传一半断掉、传上去被人改了没发现,哪个都够喝一壶的。
这篇东西我打算用一套完整的 HTML+PHP 方案,把“分片、加密、断点续传、回溯校验”这四件事一次说透。不需要引入重型的 Java 或微服务中间件,一台内网服务器、一个 PHP-FPM、一张数据表,就能把实验视频的安全归档做扎实。适合正在搭内网数据管理平台、或者被大文件上传折磨过的朋友参考。
1. 军工科研场景下的视频归档需求与整体设计思路
1.1 为什么普通上传方案在实验现场不顶用
我接触到的实验记录视频,普遍有两个特点:一是单个文件体积大,动辄 10GB 起步,病程记录、外场实测、老化试验这类场景经常要连续录制好几个小时;二是上传环境差,现场到机房的链路可能只有几十 Mbps,中间还会隔加密机、安全网关,长连接动不动就被掐断。传统的 POST 表单一把梭,要么等到超时,要么传一半网络闪断,前面几 GB 全白费,而且像抖音传作品一样只能整体重来,这在实验归档场景根本没法接受。
另一个痛点更隐蔽。实验视频属于敏感数据,不能明文裸传,也不能在服务器上落盘明文文件。明文存储意味着运维人员、备份系统、甚至一个被忽略的日志接口都可能造成泄露。后端要有加密手段,而且必须做到视频文件在传输过程和落盘存储时都不可直接读取,只有授权用户调阅时才能解密回放。
还有审计要求。军工科研平台逃不开“谁在什么时候传了什么、谁在什么时候看了什么”这条线。原来我用过不少现成的文件上传组件,开箱即用确实方便,但它们上传完就结束了,不会自动记录实验编号、设备参数、上传者身份、分片指纹这些关键元数据。真要出了质量问题想回溯,什么都查不到。所以必须把“上传”这件事当成一条完整的数据链路来设计,而不是一个孤立接口。
1.2 技术选型:HTML+PHP 能扛住吗
有人一听 PHP 就皱眉头,觉得做文件上传是大材小用。我反而觉得在这个场景下 PHP 是最合适的。内网平台普遍不追求高并发,PHP 部署简单、维护容易、一个进程就能跑起来。配合框架原生能力,处理分片接收、哈希校验、数据写库这些任务绰绰有余。
前端用 HTML5 的 File API 加 JavaScript 处理分片,这是浏览器天然支持的,不需要装任何客户端插件。实验人员只要打开浏览器,登录平台,选中视频就能自动开始分片加密上传。家属院内甚至不用配专门的工具,省去了一堆终端适配的麻烦。
服务端我坚持用 PHP 7.4 以上版本,原因很直接:openssl 扩展成熟,AES-256-GCM、RSA、哈希函数全部内置,不需要额外引一堆第三方包。密钥管理、签名验证、数据落盘加密这些关键动作,都可以在一个脚本里完成。
1.3 整体链路设计
整个系统分四个层面打配合:
- 传输层:浏览器将大文件切成若干小分片,每片单独加密后,通过 HTTPS 请求上传到 PHP 后端。后端每收一片就落盘到临时目录,并计算校验值。
- 存储层:所有分片文件落在非 Web 可达的目录,文件名不含原始信息,内容已加密。数据库保存分片索引和加密参数。
- 回滚层:实验人员可以按实验编号、录屏时间点查询历史视频,发起解密播放或下载。
- 审计层:上传、调阅、删除等操作全部写入操作日志,记录操作者、IP、设备指纹和时间戳,保证全程可追溯。
整条链路的数据流转是这样的:浏览器读文件 → JS 切片 → 每片加 AES-GCM 加密 → 逐个 POST 到 PHP → PHP 校验签名和哈希 → 临时目录落片 → 所有分片传完后触发合并 → PHP 将加密分片按序拼接成完整加密文件 → 把文件元数据和哈希写库 → 删除临时分片。后面从调阅到回放,每一步都可以通过数据库记录回溯。
2. 分片上传机制的前后端设计
2.1 前端分片逻辑:File.slice 的正确用法
分片这一步的关键参数是“片大小”。我试过 1MB、2MB、5MB、10MB,最后在 3~5MB 这个区间找到一个平衡点。片太小,请求次数暴增,每次握手和加密开销反而拖慢速度;片太大,弱网下还是容易中断。对实验视频这类几百 MB 到几十 GB 的文件,4MB 一片是我目前比较推荐的经验值,你可以通过注册一个可配置项来调整。
浏览器的 File 对象基于 Blob,切片用file.slice(start, end)即可。前端维护一个数组,记录每一片的序号和状态。切完之后,还不要直接传,先整体计算出文件的 SHA-256 校验值,这个值会伴随整个上传流程,用来做端到端完整性核对。
这里有个坑:大文件的 SHA-256 计算不能一次性读进内存。实验视频 20GB,你把它整体读到 ArrayBuffer 里,浏览器直接崩溃。正确做法是用crypto.subtle.digest配合流式读取,或者按分片逐个读取、逐片累加哈希。我习惯先按分片算每片 SHA-256,最后再用一个“哈希链接”的方式生成整文件指纹,这样既避免了内存爆炸,又让每一片都有了独立校验依据。
前端分片核心流程大概是这样:
// file 是用户选择的视频文件,CHUNK_SIZE 建议 4 * 1024 * 1024 const CHUNK_SIZE = 4 * 1024 * 1024; const fileId = crypto.randomUUID().replace(/-/g, '') + '_' + file.size; const chunkCount = Math.ceil(file.size / CHUNK_SIZE); for (let i = 0; i < chunkCount; i++) { const start = i * CHUNK_SIZE; const end = Math.min(start + CHUNK_SIZE, file.size); const blob = file.slice(start, end); // 这里对 blob 做 AES-GCM 加密,见下一章 const encryptedBlob = await encryptBlob(blob, chunkKey[i]); // 上传加密后的分片,并携带序号、哈希、fileId await uploadChunk(fileId, i, encryptedBlob); }fileId的生成要注意唯一性:同一个文件如果重传,必须复用同一个 fileId,代码里用文件大小拼在随机串后面,是为了避免两个同名同大小的文件被误判成同一个。但这个逻辑需要进一步完善,最好再叠加文件的碰撞哈希作为佐证。
2.2 服务端接收分片与断点续传设计
PHP 端接收分片时,不能只做“接住文件”这件事。每个分片都要做三件事:校验请求合法性、校验分片哈希、按序保存。我用一个upload_chunk.php接口统一接收,收到文件流后先存到一个以 fileId 命名的临时目录:
// 伪代码示意 $fileId = $_POST['file_id']; $index = (int)$_POST['chunk_index']; $hash = $_POST['chunk_hash']; $tmpDir = UPLOAD_TMP_PATH . '/' . $fileId; if (!is_dir($tmpDir)) { mkdir($tmpDir, 0700, true); } $dest = $tmpDir . '/' . sprintf('%08d.chunk', $index); move_uploaded_file($_FILES['chunk']['tmp_name'], $dest);但直接存不行,还要算一次服务端哈希,跟前端传上来的chunk_hash做比对,对不上就丢进异常队列,让前端重传这一片。这个“错误分片丢弃重传”的容错机制很关键,能挡住一些低配网络环境下的静默损坏。
断点续传的实现我推荐“前端主动探测+后端幂等响应”的组合。前端在上传之前,先调用一个check_upload.php?file_id=xxx的接口,后端扫描临时目录,返回已存在分片的序号列表。前端把已存在分片跳过,只传缺失部分。这样即使浏览器刷新、电脑重启,只要 fileId 没变,就能从断点接着传,不用重头再来。
这个机制对应到用户侧的真实体验是:实验人员在现场传视频传到 70% 网络闪断,点击“重新上传”后,不是重新读整个大文件,只补传缺失的那 30%。实测下来,这个机制能把恶劣网络环境下的上传成功率从不足五成拉高到九成五以上。
2.3 合并分片与整体校验
所有分片上传完成后,前端调用merge.php接口触发合并。合并不是简单地 cat 拼一起,因为每个分片是独立加密的,合并前要把分片按序号从 0 到 N 排序,按序写入目标文件。如果没按序号排,哪怕只有一片顺序颠倒,解密后视频也是花的。
PHP 端合并时推荐一行一行或一块一块写,不要一次性用 file_get_contents 把全部分片读进内存。我见过有人直接把 20GB 的视频一次性读进来,服务器内存直接被打满。正确做法是:
$mergedFile = STORAGE_PATH . '/' . $fileId . '.enc'; $out = fopen($mergedFile, 'wb'); for ($i = 0; $i < $chunkCount; $i++) { $chunkFile = $tmpDir . '/' . sprintf('%08d.chunk', $i); $in = fopen($chunkFile, 'rb'); stream_copy_to_stream($in, $out, CHUNK_SIZE); fclose($in); unlink($chunkFile); // 合并之后立刻清除临时分片 } fclose($out);合并完成后,后端要对整个加密文件做一次 SHA-256 计算,与前端在开始上传前上报的整文件哈希比对。如果一致,说明传输链路是完整的;如果不一致,说明丢片或者某片校验码中途被改过,这时候要把整个文件标记为“上传异常”,不允许进入档案库,同时保留现场临时分片等待重传。宁可整包重传,不能带病入库。
数据写入库之后,还有一道“隐藏动作”要做:在数据库记录中写入“文件状态:已归档”,同时触发一条审计日志。很多人在这一步偷懒,但我建议每次合并成功都要写审计日志,因为回溯的时候,这条日志可能是唯一能证明“这个文件当天确实传成功过”的证据。
3. 加密机制如何贯穿传输与存储
3.1 为什么不能只依赖 HTTPS
HTTPS 是传输加密的底座,它保证了浏览器到服务器之间这条链路上没人能窃听或篡改。但实验视频在服务器上落盘之后,HTTPS 就保护不了了。明文数据躺在磁盘上,备份系统、运维脚本、文件共享,任何一环出了问题都可能造成泄露。
所以真正的加密体系要分层:传输层用 HTTPS 保护链路,数据层用对称加密保护文件内容,密钥管理用非对称加密保护。HTTP 与 HTTPS 的关系相当于高速公路上的装甲车,而数据层加密相当于装甲车里的保险柜,两层并存才是完整的。
3.2 分片加密:AES-256-GCM 的优势
对称加密算法我推荐 AES-256-GCM,而不是很多人还在用的 AES-CBC。GCM 是带认证的加密模式,加密的同时会生成一个认证标签,解密的时候能校验数据有没有被篡改。如果只是 CBC,第三方把密文里某几个字节换掉,解密端无法感知。对实验视频这种要求完整性的数据,一定要用带认证的模式。
每一片加密的时候,都要生成一个独立的随机 IV,不能所有分片共用一个 IV。原因在于加密算法都是确定性的,处理相同明文和相同密钥时输出相同,如果 IV 复用,攻击者可以分析出分片之间的关联。IV 不要求保密,可以明文和密文一起存,但必须每个分片不同。
PHP 端用 openssl_encrypt 和 openssl_decrypt 即可:
// 加密 $iv = openssl_random_pseudo_bytes(12); $tag = ''; $ciphertext = openssl_encrypt( $plainData, 'aes-256-gcm', $aesKey, OPENSSL_RAW_DATA, $iv, $tag ); // 存储时把 $iv 和 $tag 一起保存,解密时用它们来认证前端浏览器做 AES-GCM 加密,可以直接用 Web Crypto API 的crypto.subtle.encrypt,原生支持,不需要引外部库。下面是核心片段:
async function encryptBlob(blob, aesKey, iv) { const plainBuffer = await blob.arrayBuffer(); const encryptedBuffer = await crypto.subtle.encrypt( { name: 'AES-GCM', iv, tagLength: 128 }, aesKey, plainBuffer ); return new Blob([iv, encryptedBuffer], { type: 'application/octet-stream' }); }3.3 密钥管理:RSA 包裹 AES 密钥
文件数据用 AES 密钥加密,那 AES 密钥本身怎么保护?如果每次上传都用同一个 AES 密钥,时间长了密钥泄露的风险会越来越高。正确的做法是为每个文件、甚至每个分片生成独立的 AES 密钥,然后用平台的 RSA 公钥把这个 AES 密钥包裹起来保存。
前端生成一个随机的 AES 密钥,加密完所有分片后,把 AES 密钥用 RSA 公钥加密,连同 IV、认证标签这些参数一起 POST 到后端。后端拿到后,用 RSA 私钥解开 AES 密钥,再保存到数据库或密钥管理表中。这样即使数据库被拖走,攻击者也拿不到 RSA 私钥,无法解开任何一帧视频。
用图片打个比方:AES 密钥是保险柜钥匙,RSA 公钥是锁保险柜的锁,只有掌握 RSA 私钥的授权服务端才能开锁。前端加密、服务端保管,各管一段,不会出现两端都掌握完整钥匙的情况。
密钥轮换也要纳入设计。我建议 RSA 密钥对至少半年轮换一次,轮换时对存量 AES 密钥做一次“重新包裹”运算,而不必重加密视频本身。这样既保证了长期安全性,又避免了大规模重加密带来的性能灾难。
3.4 存储层防护细节
加密文件落盘后,还要在文件系统和 Web 服务器层面做好隔离。PHP 的临时目录、加密文件目录,要全部放在 Web 根目录之外。假设站点根目录是/var/www/html,存储路径就放在/data/encrypted_videos/,并从 Nginx 或 Apache 配置上彻底禁止对这个路径的 HTTP 访问。否则就算文件加密了,攻击者把文件直接下载下来慢慢离线破解,风险依然存在。
目录权限也要注意。运行 PHP-FPM 的进程用户,要对存储目录有读写权限,但其他用户一律不能访问。实践中我会把存储目录设为0700,Owner 是www-data,并且禁止一切目录浏览。
数据库里保存的字段不要只存文件路径,至少包含这些字段:
- file_id:UUID,全局唯一
- exp_id:关联的实验编号
- orig_filename:原始文件名,仅作为展示用
- file_size:原始文件大小(明文大小)
- aes_key_wrapped:RSA 包裹后的 AES 密钥
- iv_list:每个分片的 IV 和认证标签,JSON 格式存储
- sha256_full:整文件的哈希校验值
- upload_user:上传人账号
- upload_time:上传完成时间
- file_status:状态,0 上传中、1 已归档、2 异常
我见过有的人只存路径不存 iv_list,导致后面解码的时候完全不知道用什么 IV,这个坑太低级了,但确实发生过,一定要记住:加解密参数必须跟着数据走。
4. 回溯机制:从审计日志到完整性验证
4.1 回溯到底要回什么
“回溯上传”在军工实验场景里有两层含义。第一层是断点续传的上传过程回溯,刚才已经讲过了;第二层是历史数据的事后回溯,也就是实验人员或质量管理人员,在几天、几周、甚至几年后还能精确定位到某一次实验的视频,复核当时的操作过程。这第二层才是真正的核心诉求。
具体到实现,要支持按实验编号、日期范围、设备编号、操作人员这四类条件组合查询。查询结果返回视频列表,包括文件大小、上传时间、当前状态,以及可用的调阅操作。这里很关键的一点是:列表页永远只展示元数据,视频文件本身需要通过一个受控接口才能调取,而且每次调取都要记录到审计日志中。
4.2 审计日志链路怎么建
我建议建三张表:upload_record(文件主表)、upload_chunk_record(分片记录表)、video_access_log(调阅记录表)。文件主表记录的是文件级信息,一条记录对应一个视频;分片记录表记录每一片的上传情况,包括分片序号、哈希、上传者 IP、耗时;调阅记录表则记录谁在什么时间回放了哪一段视频、用了什么授权方式。
这样设计的好处是每一层都有独立的证据链:
- 文件层:证明当前存在一份完整的视频档案。
- 分片层:证明这份档案的上传过程是完整可追踪的。
- 调阅层:证明这份档案被什么人、在什么时间看过。
实际操作中,调阅记录表还要记录用户的 UA、登录会话 ID、回放的起止时间点。如果将来发生了数据泄露或者违规调阅,这些信息能把整个链条串起来,定位到具体的操作者。这就是审计回溯的核心价值。
4.3 完整性验证链:哈希链与复合校验
前面提到合并完成后要做整文件 SHA-256 校验,但那只是静态校验。如果有攻击者把服务器上已归档的加密文件替换了,且只改文件不碰数据库里的哈希值,那么常规校验是发现不了的。所以我建议用“哈希链”的方式进一步绑定数据。
哈希链的核心逻辑:把文件的 N 个分片按顺序编号,第 0 片的哈希值直接记录,第 1 片的哈希值 = SHA256(第 1 片数据 + 第 0 片哈希值),第 2 片的哈希值 = SHA256(第 2 片数据 + 第 1 片哈希值),以此类推。这样如果攻击者篡改了第 k 片,那么从第 k 片往后所有哈希值都会对不上,验证时会立刻报警。
哈希链可以不占太多资源,只会在每次调阅时按顺序读取并计算。对实验视频来说这个成本完全可接受,毕竟不是每秒都在被调阅,但安全层级直接拉高了不止一个档次。
回溯时的调阅接口要默认走“先校验、后返回”的流程。需要调阅时,后端先读取加密文件,按哈希链逐段校验,校验通过后再解密并输出视频流。校验失败时返回明确错误码,并触发一条警告型审计日志。
5. 完整实操流程:从搭建环境到跑通一次上传
5.1 实验环境准备
我建议先搭建一个最小化验证环境。你需要一台 Linux 服务器(Ubuntu 20.04 / CentOS 7 都行),装好 Nginx/Apache 和 PHP 7.4+,并确保 openssl、fileinfo、mbstring 扩展已启用。前端不需要任何框架,纯 HTML 页面加一个 upload.html 即可。
数据库用 MySQL 或 MariaDB,建库时字符集选 utf8mb4。下面是最小表结构,实际项目可以在此基础上加实验设备表、人员表等外键关联:
CREATE TABLE upload_record ( id INT AUTO_INCREMENT PRIMARY KEY, file_id VARCHAR(64) NOT NULL UNIQUE, exp_id VARCHAR(64) NOT NULL, orig_filename VARCHAR(255) NOT NULL, file_size BIGINT NOT NULL, aes_key_wrapped TEXT NOT NULL, iv_list JSON NOT NULL, sha256_full CHAR(64) NOT NULL, upload_user VARCHAR(64) NOT NULL, upload_time DATETIME NOT NULL, file_status TINYINT NOT NULL DEFAULT 0, INDEX idx_exp_id (exp_id), INDEX idx_upload_time (upload_time) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;分片记录表可以根据需要拆分,如果数据量非常大,建议按月做分区,避免一年后查询全表扫描。
5.2 前端页面完整流程
前端页面至少需要这几个功能模块:文件选择区、上传进度显示、断点探测按钮、日志输出区。文件选择后,页面自动触发分片、加密、上传三步,不需要用户手动干预。上传完毕后自动调用 merge 接口,然后展示最终校验结果。
关键流程要封装成异步函数,让每个分片完成后立刻更新进度条。这里要特别注意:因为每个分片都是独立加密和上传的,所以进度条要以分片为粒度,而不是以字节为粒度,否则加密耗时会导致进度百分比跳变不自然。
实际项目中,我更推荐把前端上传逻辑封装成一个 Uploader 类,方便复用。此前我在另一个内网系统里复用这套逻辑时,只改了接口地址和密钥管理逻辑就上线了。前端代码全部走 HTTPS 访问,避免出现“页面上写着 secure,实际走明文”这种尴尬。
5.3 PHP 后端实现重点
后端重点是两个接口:接收分片和合并文件。接收分片时,需要统一处理四种异常:文件为空、分片序号越界、哈希不匹配、目标目录不可写。错误码要按约定返回 HTTP 状态码,比如 400 表示参数错误,409 表示分片冲突,500 表示服务器错误。前端拿到不同状态码做不同的重试策略。
合并接口需要做并发保护。避免前端重复点击“合并”按钮导致重复合并,我在合并前先查一下目标文件是否已经存在,如果存在且 status=1 就直接返回成功。同时用数据库的唯一索引 file_id 兜底,避免并发插入两条记录。
另外 PHP 运行时的 upload_max_filesize 配置,服务器端一般默认限制 2MB,但分片上传时这个限制已经不适用了,因为每个请求只传一个 4MB 以内的分片。不过还是要把 post_max_size 调到 32MB 以上,防止某些分片因加密后体积膨胀导致超出限制。加密后比明文大一点是正常的,因为 GCM 模式会附加 IV 和认证标签,这点空间开销对于安全来说是值的。
5.4 部署与配置清单
部署完成后,要按下面的清单逐项核对:
- Web 服务器禁止访问存储目录:Nginx 配置
location ^~ /data/ { deny all; }。 - PHP-FPM 运行用户对临时目录有写权限,同时临时目录要开启
open_basedir限制,避免 PHP 脚本跨目录操作。 - 数据库账号使用最小权限,只授予上传库的增删改查权限,不授予 DDL 权限。
- 启用 PHP 的错误日志,并配置 log_errors 和 error_log 路径,方便排查问题。
- 全部页面通过 HTTPS 访问,内网也建议部署内部 CA 签发的证书,不要裸跑 HTTP。
- 上传完成后,原视频文件在终端本地的清理策略,需要遵循单位保密规定。
部署完我习惯跑一次演练:用脚本模拟一个 2GB 视频的上传,传输过程中随机 kill 掉几次请求,再重启上传,确认断点续传和哈希校验能兜住。
6. 常见问题与排查实录
6.1 并发上传时分片错序或丢失
现象:多个分片同时上传,合并后的视频播放到某一帧突然花屏。
原因排查:分片请求并发时,Nginx 同时把请求打进 PHP-FPM 进程池,如果 PHP 端处理逻辑中缺少“同一 fileId 的串行处理”锁,多个进程同时操作同一个临时目录时可能发生文件覆盖或写半个文件。另一个常见原因是前端 for 循环里用了 await 并行,没有按顺序限制并发。
解决思路:前端严格限制并发数,建议同时最多 3 个分片在飞,不要 10 个 20 个一起上。PHP 后端在接收分片时加一个简单的文件锁,锁文件以 fileId 命名,处理完一个分片再释放锁,确保同一文件的分片写入互不干扰。如果你用 Redis 这类外部锁也可以,但内网环境不一定有 Redis,文件锁是最务实的方案。
6.2 合并大文件时内存溢出
现象:视频文件超过 10GB,合并时 PHP 报内存耗尽。
原因:合并代码里用了 file_get_contents 一次性读取整个分片或整个文件,或者用 file_put_contents 追加大文件。PHP 的 memory_limit 默认 128MB,光一个分片 4MB 没事,但如果把整个分片都读进内存再来回写,就很容易爆。
解决:用 stream_copy_to_stream 流式操作,一次只拷贝一小块缓冲区,而不是读取整个分片。上文给出的代码已经用了这个方式,实测 50GB 文件合并时内存占用始终稳定在 10MB 以内。
6.3 浏览器端加密后无法解密播放
现象:上传完成,后端也能读取文件,但下载后视频播放器直接报错。
原因分两类:一类是前端加密后的二进制处理错误,比如把 IV 也混入了密文却没有在解密端正确切分;另一类是密钥和 IV 存储与读取不匹配。GCM 模式解密时,IV 和认证标签任何一个不对,解密结果都是乱码,AVPlayer 根本读不出帧。
排查建议:先在本地生成一段 1 分钟的测试小视频,走完整加密解密流程,解密后用 FFmpeg 检查是否能正常读取。如果仍旧失败,用 openssl 命令手动对比前后端密钥是否一致,确认后再怀疑编码器问题。
6.4 上传中断后被误判为上传完成
现象:网络中断,但前端因某种原因没有感知,直接调用 merge 接口,后端生成了一个不完整的文件。
原因:前端对上传结果的处理过于乐观,没有等所有分片 Promise 全部 resolve 就触发合并。
解决:前端在上传完成后必须等待所有分片全部返回成功状态,然后再调用 merge。更保险的做法是,前端在 merge 前主动向后端发一个“预合并检查”请求,后端扫描分片目录,核对分片数量是否等于声明数量,如果数量对不上就返回 code 提示缺片,前端补传后再 merge。
6.5 密钥丢失导致历史视频永远无法解密
现象:数据库里的 aes_key_wrapped 字段被误清空,或者 RSA 私钥在轮换时没做备份,导致历史上所有加密视频变成死数据。
原因:这是最致命的管理事故。RSA 私钥一旦丢失,加密数据就永远无法解密,比文件损坏更严重。
解决:RSA 私钥必须做离线备份,备份介质单独存放,并制定严格的取用审批流程。日常运维中,禁止从生产环境直接导出私钥;任何密钥轮换操作都要先验证备份可用,再执行轮换。这也是我建议每半年轮换一次但绝不跨年轮换的原因,毕竟再完善的制度也比不过频繁操作带来的失误。
写在最后
这套方案我陆陆续续打磨了好几版,从最早一块裸传,到后来慢慢加上分片、断点、加密、审计,每一步都是被实际问题逼出来的。刚开始在实验室试跑时,也经常遇到并发错序、密钥丢失、合并超时这种问题,但你把这些坑一个个趟平之后,整套系统会变得非常可靠。后来放到实验现场让一线人员用,除了偶尔反馈“上传速度怎么变慢了”,基本没再出过严重事故——这种稳定性,才是实验数据管理最值钱的部分。
如果你是第一次搭这种平台,我的建议是先别急着上完整功能,第一步先用原生 HTML5 File API 把一个几十 MB 的小文件分片传起来,验证前端切片、后端接片的基本链路;第二步加上 AES-GCM 加密和解密,跑通“加密上传-解密回放”;第三步再做断点续传和审计日志,补全管理能力。一步步来,比一次性铺开容易排查问题得多。
这套设计里还有很多可以扩展的点,比如把审计日志接入可视化大屏、增加视频关键帧的自动抓取和比对、对接实验设备自动同步录制元数据等等。只要分片、加密、审计这条主线不乱,上层能力想怎么加都只是时间问题。希望这篇东西能帮你少走几步弯路。