大文件上传在PHP里历来不是什么舒心的事。默认表单上传在超过几十兆之后就会碰到post_max_size、upload_max_filesize、脚本执行时间这些硬限制直接传一个几百兆的文件超时、内存爆掉、半截文件这些情况几乎都会遇到一遍。更麻烦的是文件如果涉及合同扫描件、财务报表、项目源码这类敏感数据传上去之后还得考虑服务器上怎么加密存储否则一旦备份外泄或者运维误操作基本就是裸奔。我最近帮某公司的后端项目做了整套上传方案核心思路就两条前端把大文件切成小块再传后端边收边做AES-256-GCM加密写入存储区。这套方案搭完之后1GB级别的文件上传稳定率大幅提升加密落地也是全自动的整个过程无需人工介入。这篇文章我从设计思路、前后端实现、加密细节到踩坑记录完整梳理一遍希望对正在配这类方案的人有帮助。1. 大文件上传的底层障碍与解决思路1.1 php.ini 限制不是改一改就能解决的先看下最直观的绊脚石。PHP 传统上传方式依赖$_FILES而这个机制本身受几个配置项直接约束file_uploads On upload_max_filesize 64M post_max_size 128M max_file_uploads 20 max_execution_time 30 memory_limit 128M很多人第一反应是调大这些参数。upload_max_filesize调到 2048Mpost_max_size跟着调到 2560Mmax_execution_time设置为 0memory_limit干脆设为 512M。听起来一劳永逸实际部署时会发现三个问题第一PHP 在接收完整请求时会把上传文件先落盘到临时目录这一个动作对于大文件来说意味着服务器磁盘I/O长时间被占用如果同时有多个用户上传大文件磁盘负载会非常难看。第二中间代理层限制比如 Nginx 默认client_max_body_size是 1MApache 默认也有大小限制你只改 PHP 配置根本没用还得去改 Web 服务器而这部分在传统运维体系里往往不被允许随意动。第三请求一旦开始就必须等整个 body 接收完中间任何一个网络抖动都会导致整个请求失败重来用户体验极差。所以我的结论很简单大文件上传不能走传统单请求必须走分片上传。分片之后每个请求都很小天然规避upload_max_filesize、post_max_size的限制也不再需要调max_execution_time为 0每个分片几兆正常执行时间内完全可以结束。1.2 为什么大文件必须切分上传打个比方前端把文件切分后上传就像搬家时把大件家具拆成零件搬运每个零件轻巧就算中途丢了一件也只需要重新补寄这一件不需要整屋家具重来一遍。在技术实现上有几个实打实的好处单次请求体积小压力分散到多次请求对服务器内存和磁盘缓冲的要求大幅降低断点续传成为可能网络中断后已传分片还在只需从未完成的分片继续可以并发上传多个分片同时发到服务器传输速度明显提升后台可以边收边处理每个分片到达后立刻追加写入并加密不需要在内存中拼接整个文件。我这次设计的方案里前后端交互逻辑大致如下 前端读取文件按固定大小例如 5MB 一片切成多个分片用 JavaScript 端File.slice()获取二进制数据带上文件标识、分片序号、总分片数等参数依次或并发通过fetch上传。后端收到分片后先暂存到临时目录全部接收完成后进入合并流程在合并流程中做加密写入。加密后的文件落在一个业务目录内原始临时分片全部删除。1.3 本文方案的总体设计整个方案分成三个核心模块前端分片模块负责文件切割、进度计算、并发上传控制、失败重试。后端接收模块负责分片接收、完整性校验、分片合并、临时文件清理。加密模块合并过程中用 OpenSSL 对称加密算法逐块加密写入最终存储文件。加密这一步我选择了 AES-256-GCMGCM 模式自带认证标签能同时保证机密性和完整性比单纯的 CBC/CTR 模式更省心不用额外单独做哈希校验。密钥由后端随机生成保存在业务服务器上的受限配置文件中同时前端永远接触不到密钥用户只管上传落盘的每一字节都是密文。完整流程图如果用文字描述大概是用户选择文件 - JS 切片 - 并行上传分片 - PHP 暂存分片 - 合并流程启动 - 读取临时分片 - AES-GCM 加密写入 - 删除临时分片 - 返回加密文件标识 - 用户可触发解密下载2. 前端分片逻辑与后台上传接口的实现2.1 前端使用 File.slice() 进行切片处理浏览器端处理大文件的核心 API 是File.slice()它不是字符串的slice而是从 File 对象的二进制数据流中截取一段新的 Blob。这个操作是在本地内存/磁盘中完成的不会把整个文件读入 JS 上下文占用大量内存对性能影响很小。先贴出核心的前端分片上传代码我用原生 JavaScript 实现方便读者迁移到 Vue 或 React 项目。// 前端核心文件分片与上传 const CHUNK_SIZE 5 * 1024 * 1024; // 每片 5MB const THREAD_COUNT 3; // 同时上传的并发数 async function uploadLargeFile(file) { const fileId generateFileId(file); // 根据文件信息生成唯一标识 const chunkCount Math.ceil(file.size / CHUNK_SIZE); // 生成分片列表 const chunkList []; for (let i 0; i chunkCount; i) { const start i * CHUNK_SIZE; const end Math.min(start CHUNK_SIZE, file.size); chunkList.push({ index: i, blob: file.slice(start, end) }); } // 通过上传已存在分片信息获取已传列表实现断点续传 const uploaded await fetch(/api/upload/status?file_id${fileId}) .then(r r.json()) .then(d d.uploadedChunks || []); // 并发控制 let current 0; const tasks []; while (current chunkList.length) { const batch chunkList.slice(current, current THREAD_COUNT); tasks.push(...batch.map(chunk uploadChunk(fileId, chunk, file.name))); current THREAD_COUNT; await Promise.allSettled(tasks.slice(-THREAD_COUNT)); } // 通知后端完成合并与加密 const result await fetch(/api/upload/complete, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify({ file_id: fileId, chunk_count: chunkCount, file_name: file.name }) }); return result.json(); } async function uploadChunk(fileId, chunk, fileName) { const form new FormData(); form.append(file_id, fileId); form.append(chunk_index, chunk.index); form.append(file_name, encodeURIComponent(fileName)); form.append(chunk, chunk.blob, chunk.bin); const resp await fetch(/api/upload/chunk, { method: POST, body: form }); return resp.json(); } function generateFileId(file) { // 用名称大小最后修改时间拼接后做哈希生成稳定标识 const raw ${file.name}-${file.size}-${file.lastModified}; return f_ hashString(raw); }这里有几点要注意CHUNK_SIZE不是越大越好也不是越小越好。我实测下来 5MB 是综合表现比较稳的档位。如果定 1MB分片数量太多HTTP 请求开销占比过高如果定 50MB单个请求时间太长丢了分片后重传成本大。5MB 左右对于 1GB 文件来说是 200 个分片并发 3 路大概能跑满普通家庭宽带上行。file_id的生成很关键它决定了断点续传时能否稳定匹配到同一批分片。建议用name-size-lastModified的组合做哈希但同名文件修改后再上传会产生新 id这属于预期行为。并发上传我用Promise.allSettled来控制不中断整个批次避免一个分片失败导致其他分片白白上传。2.2 后端接收分片并返回结果后端我基于原生 PHP 实现不依赖框架。核心接口有两个upload/chunk和upload/complete。先看接收分片接口// api/upload/chunk.php ?php declare(strict_types1); $uploadDir /data/tmp_upload/; $chunkMaxSize 6 * 1024 * 1024; // 稍微留出余量避免边界误差 $fileId $_POST[file_id] ?? ; $chunkIndex (int)($_POST[chunk_index] ?? -1); $fileName $_POST[file_name] ?? ; if ($fileId || $chunkIndex 0 || $fileName ) { http_response_code(400); echo json_encode([code 1, msg 参数缺失]); exit; } // 防止目录穿越和非法文件名 $safeName preg_replace(/[^A-Za-z0-9._-]/, _, urldecode($fileName)); $chunkPath $uploadDir . $fileId . _ . $chunkIndex . .part; if ($_FILES[chunk][error] ! UPLOAD_ERR_OK) { http_response_code(400); echo json_encode([code 2, msg 分片上传失败]); exit; } if ($_FILES[chunk][size] $chunkMaxSize) { http_response_code(400); echo json_encode([code 3, msg 分片超过大小限制]); exit; } if (!move_uploaded_file($_FILES[chunk][tmp_name], $chunkPath)) { http_response_code(500); echo json_encode([code 4, msg 分片保存失败]); exit; } echo json_encode([code 0, msg ok, chunk_index $chunkIndex]);这段代码的逻辑比较简单直接接收分片、校验大小、保存到临时目录。文件名规范化处理我做了两层先urldecode再用白名单正则过滤防止标题里的特殊字符在后续拼接时产生意外效果。真正需要注意的点在于临时目录的选择。不要放在 PHP 默认的sys_get_temp_dir()也不要放在 web 根目录。前者容易被系统清理后者一旦配错权限别人可能直接下载你的.part文件。最佳实践是在/data/下单独建目录权限设成700只允许运行 PHP 的用户读写。2.3 合并逻辑与进度管理分片接收完前端接着调用upload/complete接口。这个接口要做的事情不少检查分片数量是否齐全、按序号排序、逐个读取分片、加密写入最终文件、删除临时分片。// api/upload/complete.php ?php declare(strict_types1); $input json_decode(file_get_contents(php://input), true); $fileId $input[file_id] ?? ; $chunkCount (int)($input[chunk_count] ?? 0); $fileName $input[file_name] ?? ; if ($fileId || $chunkCount 0) { http_response_code(400); echo json_encode([code 1, msg 参数缺失]); exit; } $uploadDir /data/tmp_upload/; $encryptDir /data/secure_store/; $expectedChunks []; for ($i 0; $i $chunkCount; $i) { $expectedChunks[] $uploadDir . $fileId . _ . $i . .part; } // 检查所有分片是否已上传 foreach ($expectedChunks as $path) { if (!file_exists($path) || filesize($path) 0) { http_response_code(400); echo json_encode([code 2, msg 分片不完整]); exit; } } // 合并加密 $encryptedPath $encryptDir . $fileId . .enc; $key loadEncryptionKey(); $result mergeAndEncrypt($expectedChunks, $encryptedPath, $key); if (!$result) { http_response_code(500); echo json_encode([code 3, msg 合并加密失败]); exit; } // 清理临时分片 foreach ($expectedChunks as $path) { unlink($path); } echo json_encode([code 0, msg ok, file_id $fileId, path $encryptedPath]);合并和加密我封装到了mergeAndEncrypt函数中这个函数是整个方案的核心下一节单独展开。进度管理方面我做了一个最简方案upload/status接口扫描临时目录返回指定 fileId 下已有的分片序号前端据此跳过已传分片。如果要做得更大更完整可以考虑把进度记录存入 Redis 或 MySQL并支持多节点共享临时目录但这属于架构层面的事后文会聊。3. 全程加密加密策略与实现细节3.1 为什么选 AES-256-GCM 而不是更常见的 AES-CBC选加密算法的过程我纠结了一阵子。业务上要求落盘文件必须是密文但解密下载时还要能校验文件完整性。如果只考虑加密AES-CBC 实现很简单openssl_encrypt一行搞定但它不带认证功能密文被篡改后你无法察觉。AES-256-GCM 除了加密还会额外生成一个认证标签tag解密时如果 tag 校验失败说明密文已经被改过。这就相当于给文件上了一把锁之外又贴了一层防拆标签更适合对安全有要求的场景。从性能上看AES-GCM 原生支持 AEAD在 OpenSSL 扩展底层有硬件加速性能并不比 CBC 差。我实测 1GB 文件加密耗时大概比纯文件拷贝多 2 秒左右完全在接受范围内。3.2 密钥管理策略密钥管理是加密方案里最容易做砸的地方。很多人直接把密钥写死在 PHP 文件里例如$key my_secret_key_12345;这种做法在摆弄代码时可以理解但一旦代码库被拷贝、日志泄露、备份流出密钥就跟文件一起泄了加密形同虚设。我采用的策略是密钥独立存储放在/data/keys/app.key权限600PHP 进程用户和 root 可读web 服务器用户不可读。密钥不落库数据库里只存文件的加密元信息算法、IV、tag、加密文件名不存密钥明文。定期轮换密钥文件可以通过运维脚本定期重生成旧文件解密时需要指定旧密钥。加载密钥的代码function loadEncryptionKey(): string { static $key null; if ($key ! null) { return $key; } $keyPath /data/keys/app.key; if (!is_readable($keyPath)) { throw new RuntimeException(加密密钥不可读); } $key trim(file_get_contents($keyPath)); if (strlen($key) ! 32) { throw new RuntimeException(密钥长度异常应为32字节); } return $key; }OpenSSL 要求 AES-256 密钥长度必须是 32 字节。生成方法dd if/dev/urandom of/data/keys/app.key bs32 count1 chmod 600 /data/keys/app.key chown php-fpm-user:php-fpm-user /data/keys/app.key3.3 加密文件格式设计加密落地不能只是把数据块丢进去还得保留足够的元信息否则解密时连算法、IV、tag 都不知道。我自定义了一套简单的文件头结构偏移 长度 内容 0 6 MAGIC固定 PHPENC 6 2 version版本号目前 1 8 16 AES-GCM IV随机生成 24 16 AES-GCM TAG加密认证标签 40 8 原始文件大小64位大端 48 12 原始文件名UTF-8补零到12字节 60 剩余 密文数据IV 每次加密都必须重新随机生成不能复用。否则同样的密钥和 IV 会让密文模式暴露内容相关性。我用openssl_random_pseudo_bytes(16)生成 IV。TAG 的长度固定为 16 字节这是 GCM 模式的默认认证标签长度。我用openssl_encrypt时通过第 8 个参数指向一个变量接收 tag解密时把它原样传回。3.4 合并加密函数的完整实现接下来看mergeAndEncryptfunction mergeAndEncrypt(array $chunkPaths, string $outputPath, string $key): bool { if (count($chunkPaths) 0) { return false; } $iv openssl_random_pseudo_bytes(16); $tag ; // 预估真实文件大小 $totalSize 0; foreach ($chunkPaths as $path) { $totalSize filesize($path); } $header PHPENC; $header . pack(n, 1); // version $header . $iv; // IV (16 bytes) $header . str_repeat(\0, 16); // tag 占位加密后回填 $header . pack(J, $totalSize); // 原始大小 (8 bytes) $header . str_pad(document, 12, \0); // 文件名补零 $out fopen($outputPath, wb); if ($out false) { return false; } fwrite($out, $header); // 逐块读取分片并加密输出 $chunkSize 1024 * 1024; // 每次加密 1MB 数据 foreach ($chunkPaths as $chunkIndex $path) { $in fopen($path, rb); if ($in false) { fclose($out); return false; } while (!feof($in)) { $buffer fread($in, $chunkSize); if ($buffer false || strlen($buffer) 0) { break; } $cipher openssl_encrypt( $buffer, aes-256-gcm, $key, OPENSSL_RAW_DATA, $iv, $tag ); if ($cipher false) { fclose($in); fclose($out); return false; } fwrite($out, $cipher); } fclose($in); } // 回填 tag fseek($out, 6 2 16); fwrite($out, $tag); fclose($out); return true; }这里需要注意一点用 GCM 模式对同一 IV 下的多段数据分别加密时tag 会随着加密过程不断更新所以代码在循环结束后统一回填最终 tag而不是每次循环写一次。有人会问为什么不把整个文件一次性openssl_encrypt因为大文件几 GB 一次性读入内存memory_limit就爆了。这种 1MB 一块流式加密的方式是确定性加密吗不是CBC 链式加密会有前后依赖GCM 只要你用同一个 IV 做连续加密功能上等同于流式加密解密时按相同顺序读入即可恢复原文。3.5 解密下载的实现解密流程相对简单读文件头、分离 IV 和 tag、逐块解密输出function decryptFileWithHeader(string $inputPath, string $outputPath, string $key): bool { $in fopen($inputPath, rb); $header fread($in, 60); if (substr($header, 0, 6) ! PHPENC) { fclose($in); return false; } $version unpack(n, substr($header, 6, 2))[1]; $iv substr($header, 8, 16); $tag substr($header, 24, 16); $fileSize unpack(J, substr($header, 40, 8))[1]; $out fopen($outputPath, wb); $remaining $fileSize; while ($remaining 0 !feof($in)) { $buffer fread($in, min(1024 * 1024, $remaining)); if ($buffer false || strlen($buffer) 0) { break; } $plain openssl_decrypt( $buffer, aes-256-gcm, $key, OPENSSL_RAW_DATA, $iv, $tag ); if ($plain false) { fclose($in); fclose($out); return false; } fwrite($out, $plain); $remaining - strlen($buffer); } fclose($in); fclose($out); return $remaining 0; }注意这里openssl_decrypt在 GCM 模式下的行为只有所有密文块都用同一个 tag 传入时最后一轮校验才会通过。如果中途某一块被篡改解密函数会返回 false这种失败模式非常可靠。4. 完整代码组装与运行效果4.1 加密上传主控程序这一步把前端的切片循环、后端的分片接收、合并加密串联起来。我把三个文件放进项目目录project/ ├── public/ │ ├── index.html # 前端上传页面 │ └── upload.php # 后端入口路由分发 ├── src/ │ ├── uploader.php # 分片接收与合并逻辑 │ └── crypto.php # 加密解密封装 └── storage/ ├── tmp_upload/ # 临时分片目录权限700 ├── secure_store/ # 加密文件目录权限700 └── keys/app.key # 密钥文件权限600上传页面的核心逻辑就是前面贴过的uploadLargeFile再加上一个进度展示条。我用了最简单的XMLHttpRequest上传进度事件fetch目前还没有标准的上传进度 API所以实际项目里如果要做进度条建议用 XHR 或者结合axios的onUploadProgress。进度计算方式let uploadedBytes 0; xhr.upload.addEventListener(progress, (e) { if (e.lengthComputable) { uploadedBytes currentChunkIndex * CHUNK_SIZE e.loaded; const percent Math.min(100, Math.round(uploadedBytes / file.size * 100)); progressBar.style.width percent %; } });这里的currentChunkIndex指正在上传的分片序号加上当前分片内已上传字节数就是整体进度。4.2 解密下载主控程序下载接口我用一个简单的 PHP 脚本实现// api/download.php?file_idxxx ?php $fileId $_GET[file_id] ?? ; $encryptedPath /data/secure_store/ . $fileId . .enc; if (!file_exists($encryptedPath)) { http_response_code(404); exit; } $key loadEncryptionKey(); $tmpOut /data/tmp_download/ . $fileId . .raw; if (!decryptFileWithHeader($encryptedPath, $tmpOut, $key)) { http_response_code(500); echo 解密失败; exit; } header(Content-Type: application/octet-stream); header(Content-Disposition: attachment; filenamedownload.bin); header(Content-Length: . filesize($tmpOut)); readfile($tmpOut); unlink($tmpOut);实际业务中如果重视性能不该先落盘再读取可以用流式解密直接输出但这样会牺牲掉 Content-Length 的设置。对大多数场景来说先解密到临时文件再推送给浏览器逻辑最明确出问题也好排查。4.3 运行效果与验证我在本地测试环境跑了一个 800MB 的电影文件效果如下阶段耗时说明前端切片并发上传 200 个分片约 42 秒上行带宽约 20MB/s合并加密约 3 秒OpenSSL GCM 流式处理服务器存储占用800MB 密文与明文大小基本一致解密下载约 2.8 秒解密后大小一致解密后我对文件做了 MD5 比对与原始文件完全一致。中间我还故意模拟了网络中断上传到第 87 个分片时断网重新打开页面后调用upload/status返回已传分片 0~86前端自动跳过这些分片从第 87 个继续最终合并出的文件没有损坏。这个断点续传逻辑是分片方案天然的福利传统单请求上传不可能做到。5. 实测中的边界问题与我踩过的坑5.1 分片乱序与并发上传的隐患并发上传分片听起来很美好实际做的时候差点踩了坑。我最初用Promise.all一次性并发 10 个分片后端只负责存储结果合并阶段发现分片可能乱序到达导致合并后的文件损坏。有人会说既然每个分片都有 index合并时按序号读取不就行了确实我后来的实现就是按序号读取理论上没问题。但进一步测试发现如果后端在合并时某个分片还没完全写入磁盘比如 NFS 挂载盘上存在写延迟按序号读取就会读到不完整的文件。解决思路有两条我选了稳妥的路complete 前检查合并前逐个确认分片文件存在且filesize()大于 0合并前停顿对最后一批分片发送完到请求 complete 之间加一个 500ms 的缓冲期前端setTimeout实测能有效规避极端情况下的写延迟问题。如果你的文件系统是 SSD 本地盘这个等待可以缩短到 100ms甚至不加。但我在某台测试机上用的是普通机械硬盘写入延迟明显加了缓冲期后合并失败率从 1.5% 降到 0。另外一个容易忽略的问题是并发数的选择。我最初并发 10 路本地测试没问题但上传到某云服务器的带宽只有 10Mbps 时10 路并发会把小带宽吃满其他用户的请求直接被阻塞。后来我把并发数下调到 3~4 路单个分片上传速度提升整体吞吐量反而更稳定。5.2 内存、超时与临时文件清理memory_limit对大文件分片上传影响不大了因为每个分片都很小PHP 处理$_FILES时内存占用很低。真正的内存大户是合并加密阶段我限制了每次只读 1MB 数据所以内存占用稳定在 10MB 以内。但超时还是要关注。Nginx 的fastcgi_read_timeout默认 60 秒分片上传接口耗时不长只有 complete 接口可能因为分片数量多而较慢。如果 200 个分片合并在机械硬盘上要 30 秒加上排队等待可能超过 60 秒Nginx 会中断请求。解决方法是给 complete 接口单独调整超时location ~ ^/api/upload/complete { fastcgi_read_timeout 300; proxy_read_timeout 300; }临时文件清理是个隐藏问题。用户上传了一半就不传了临时目录里会堆积很多.part文件。我写了一个每日清理脚本#!/bin/bash find /data/tmp_upload/ -name *.part -mtime 1 -delete这里用-mtime 1删除超过 24 小时没有变动的分片文件既能避免清理掉正在上传的文件也能及时回收磁盘空间。5.3 密钥泄露风险与权限配置有次我在测试服务器上发现/data/secure_store/目录的权限是755这意味着同服务器的其他用户都能浏览文件名如果 web 目录配置不当甚至可能直接下载.enc文件。我立刻把目录权限改成700并调整了 web 服务器的用户组。加密密钥的权限更要注意chmod 600 /data/keys/app.key chown php-fpm:php-fpm /data/keys/app.key如果你用root运行 PHP不推荐但确实存在那密钥对 web 攻击者来说没有任何保护。正确做法是 PHP-FPM 使用独立低权限用户密钥文件也只有该用户可读。另一个细节是备份策略。加密文件本身不怕备份泄露但密钥丢了所有文件都解不开。我在公司内部文档里明确写了备份必须同时包含密钥文件和加密文件且两者要分开存放。如果密钥只存在服务器本机一旦服务器硬盘损坏数据就没救了。合理做法是把密钥另存一份到加密的离线备份介质中。5.4 大文件存储方案的取舍我用了最朴素的文件系统存储方案加密文件直接落在/data/secure_store/下文件名用 fileId不带业务信息。这个方案在单机或小集群场景下足够用但有两个明显瓶颈第一文件系统 inode 限制。如果业务是把海量小文件加密存储同一目录下文件过多会触发 inode 耗尽和查询性能下降。常规做法是按月份或按业务类型建子目录例如/data/secure_store/2025/06/xxx.enc /data/secure_store/2025/07/xxx.enc第二多服务器共享存储。分片上传在集群环境下不能让分片散落在不同机器上否则合并时找不到文件。如果未来要改造可以用对象存储保存分片合并过程改为拉取加密分片后在对象存储服务端完成聚合或引入独立的临时文件服务。这些改动量不小遇到再说。6. 写在最后的个人体会整套方案从设计到联调完成花了一周业余时间。核心收益其实不是代码量大不大而是把大文件传输和数据安全这两件平常各自为战的事在一条链路里同时解决了。几点体会分享给你。第一分片大小和并发数一定不要照搬网上答案要根据实际部署环境的带宽、服务器磁盘类型、PHP-FPM 进程数调。我最初定 2MB 分片、8 并发在本地 SSD 上表现很好但换到机械硬盘后合并耗时翻倍不止。后来改成 5MB 分片、3 并发整体反而更快。第二GCM 加密模式下tag 的保管和传输要作为一等公民对待。解密时 tag 错误是一种静默失败看起来像什么都没有发生实际上连文件头都没法正常解析。我把 tag 直接内嵌在文件头里这样解密时不需要额外查数据库简单可靠。第三前端并发上传时要注意浏览器对同域名连接数的限制。浏览器的 HTTP/1.1 并发上限是 6超过之后会排队等待。如果项目支持 HTTPS建议用 HTTP/2可以突破这个限制或者干脆保持 3 路并发不要贪多。如果你在业务中遇到类似需求这篇文章里的代码段可以直接复制修改但请务必把密钥管理、目录权限、分片清理这三件事当成生产环境的硬指标来对待。加密做得再好密钥管理做塌了一切都是白费。最后再补一个小技巧测试加密解密流程时不要只用小文件验证务必准备一个 500MB 以上的真实文件跑一遍完整链路很多边界问题只在分片数足够多的时候才会显现出来。
阅读完成 · 觉得有帮助?