PHP实现≤4MB字节范围文件拆分的方案咨询(适配Outlook上传)
我之前做过类似的Outlook大附件上传需求,用PHP实现文件拆分上传的核心其实就是按字节范围分片读取文件,然后配合Graph的上传会话接口逐个上传分片。给你整理了一套完整的方案,包含代码示例和关键注意点:
PHP实现Outlook大附件分片上传方案
核心思路
- 先确定单块最大尺寸:建议设为3.8MB(3984588字节),留足HTTP头的额外空间,避免触发4MB的邮件服务器限制
- 通过
filesize()获取文件总大小,计算需要拆分的分片数量 - 逐片读取文件的字节范围,构造符合Graph要求的
Content-Range请求头,上传每个分片 - 所有分片上传完成后,调用上传会话接口完成整个附件的上传
代码实现步骤
1. 定义基础配置
// 单块最大字节数(3.8MB,预留缓冲避免超4MB) define('CHUNK_SIZE', 3984588); // 从Graph创建上传会话后得到的上传URL $uploadUrl = "你的上传会话URL"; // 要上传的文件路径 $filePath = "/path/to/your/large/file.pdf";
2. 初始化文件与分片参数
// 以二进制模式打开文件,避免内容损坏 $fileHandle = fopen($filePath, 'rb'); if (!$fileHandle) { die("无法打开文件,请检查路径或权限"); } // 获取文件总大小 $fileSize = filesize($filePath); // 计算总分片数(向上取整) $totalChunks = ceil($fileSize / CHUNK_SIZE);
3. 循环上传每个分片
for ($chunkIndex = 0; $chunkIndex < $totalChunks; $chunkIndex++) { // 计算当前分片的字节起始和结束位置 $startByte = $chunkIndex * CHUNK_SIZE; $endByte = min(($chunkIndex + 1) * CHUNK_SIZE - 1, $fileSize - 1); // 定位到当前分片的起始位置 fseek($fileHandle, $startByte); // 读取当前分片内容 $chunkContent = fread($fileHandle, CHUNK_SIZE); // 构造请求头,Content-Range是Graph要求的必填项 $headers = [ "Content-Type: application/octet-stream", "Content-Range: bytes $startByte-$endByte/$fileSize" ]; // 用CURL发送POST请求上传分片 $ch = curl_init($uploadUrl); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $chunkContent); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); // 生产环境务必开启 $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); // Graph返回202表示分片上传成功,最后一块可能返回201 if (!in_array($httpCode, [202, 201])) { die("第{$chunkIndex+1}块上传失败,HTTP状态码:$httpCode,响应内容:$response"); } echo "第{$chunkIndex+1}/{$totalChunks}块上传成功\n"; }
4. 完成上传会话(可选,部分场景最后一块上传后自动完成)
如果所有分片上传后未自动触发完成,需要发送收尾请求:
$finalHeaders = [ "Content-Range: bytes */$fileSize" ]; $ch = curl_init($uploadUrl); curl_setopt($ch, CURLOPT_PUT, true); curl_setopt($ch, CURLOPT_HTTPHEADER, $finalHeaders); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); $finalResponse = curl_exec($ch); $finalHttpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($finalHttpCode == 201) { echo "文件上传完成!"; } else { die("上传会话收尾失败,HTTP状态码:$finalHttpCode,响应内容:$finalResponse"); } // 关闭文件句柄 fclose($fileHandle);
关键注意事项
- 分片大小选择:绝对不要设为刚好4MB,HTTP请求头会占用额外字节,导致单请求总大小超过限制,建议预留10%-20%的缓冲
- 二进制读取:必须用
'rb'模式打开文件,避免Windows系统下的换行符转换导致文件内容损坏 - 重试机制:建议给分片上传添加重试逻辑(比如失败后重试3次),避免网络波动导致上传失败
- 状态记录:如果是超大文件,建议记录每个分片的上传状态,避免中断后重新上传所有分片
- SSL验证:生产环境请保持
CURLOPT_SSL_VERIFYPEER为true,避免安全风险
内容的提问来源于stack exchange,提问作者Oliver
相关产品推荐
相关产品推荐

