使用fread读取大文件致内存不足,求Backblaze B2上传优化方案
嘿,我完全懂你的痛点——一次性把大文件塞进内存简直是内存杀手!其实Backblaze B2专门提供了大文件分块上传的机制,完美解决这个问题,不用把整个文件加载到内存里。咱们一步步来拆解怎么做:
为什么原来的代码会出问题?
你原来的代码用
fread($handle, filesize($my_file))直接把整个文件读进内存,当文件超过PHP的内存限制(通常默认是128MB)时,必然会触发内存不足的错误。
解决方案:B2大文件分块上传
B2允许将大文件拆分成多个至少5MB的块(建议用100MB,刚好避开你的问题阈值),逐个上传这些块,最后再把它们合并成完整文件。这样每次只需要在内存里保留一个块的数据,内存占用就可控了。
步骤1:准备基础配置
首先确保你已经获取了B2的API授权信息(accountId、applicationKey、bucketId这些),然后初始化必要的API请求。
步骤2:手动实现分块上传代码
这里给你一个底层实现的示例(如果用官方SDK会更简单,后面会提):
<?php $file_name = "file.txt"; $my_file = "<path-to-file>" . $file_name; $chunkSize = 100 * 1024 * 1024; // 100MB 每块 $fileHandle = fopen($my_file, 'rb'); $fileSize = filesize($my_file); // 1. 启动大文件上传 $startLargeFileUrl = "https://api.backblazeb2.com/b2api/v2/b2_start_large_file"; $startPayload = json_encode([ "bucketId" => "YOUR_BUCKET_ID", "fileName" => $file_name, "contentType" => "text/plain" // 根据你的文件类型调整 ]); // 发送请求(替换成你的授权令牌) $startResponse = sendB2Request($startLargeFileUrl, $startPayload); $fileId = $startResponse->fileId; // 2. 分块读取并上传 $chunkIndex = 0; $sha1List = []; while (!feof($fileHandle)) { $chunk = fread($fileHandle, $chunkSize); if (empty($chunk)) break; // 计算当前块的SHA1哈希(B2要求上传块时提供) $chunkSha1Hex = bin2hex(sha1($chunk, true)); $sha1List[] = $chunkSha1Hex; // 获取当前块的上传URL $getUploadPartUrl = "https://api.backblazeb2.com/b2api/v2/b2_get_upload_part_url"; $getPartPayload = json_encode(["fileId" => $fileId]); $partUrlResponse = sendB2Request($getUploadPartUrl, $getPartPayload); $uploadUrl = $partUrlResponse->uploadUrl; $authorizationToken = $partUrlResponse->authorizationToken; // 上传当前块 $uploadHeaders = [ "Authorization: $authorizationToken", "Content-Type: application/octet-stream", "X-Bz-Part-Number: " . ($chunkIndex + 1), "X-Bz-Content-Sha1: $chunkSha1Hex" ]; $uploadResponse = sendB2UploadRequest($uploadUrl, $chunk, $uploadHeaders); $chunkIndex++; } fclose($fileHandle); // 3. 完成大文件上传 $finishLargeFileUrl = "https://api.backblazeb2.com/b2api/v2/b2_finish_large_file"; $finishPayload = json_encode([ "fileId" => $fileId, "partSha1Array" => $sha1List ]); $finishResponse = sendB2Request($finishLargeFileUrl, $finishPayload); // 辅助函数:发送B2 JSON API请求 function sendB2Request($url, $payload) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $payload); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, [ "Authorization: YOUR_AUTH_TOKEN", // 替换成你的授权令牌 "Content-Type: application/json" ]); $response = curl_exec($ch); curl_close($ch); return json_decode($response); } // 辅助函数:发送块上传请求(二进制数据) function sendB2UploadRequest($url, $data, $headers) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); $response = curl_exec($ch); curl_close($ch); return json_decode($response); } ?>
更省心的方式:使用官方PHP SDK
如果你不想自己写这么多底层代码,Backblaze官方有PHP SDK,已经内置了大文件分块上传的功能,一行代码就能搞定,而且自动处理内存管理:
<?php use BackblazeB2\BackblazeB2; $b2 = new BackblazeB2([ 'accountId' => 'YOUR_ACCOUNT_ID', 'applicationKey' => 'YOUR_APPLICATION_KEY', ]); $b2->uploadLargeFile([ 'bucketId' => 'YOUR_BUCKET_ID', 'fileName' => $file_name, 'filePath' => $my_file, 'partSize' => 100 * 1024 * 1024 // 100MB每块 ]); ?>
这个SDK会自动分块读取文件,每次只加载一个块到内存,完全不用你手动处理分块、哈希计算和API请求,非常省心。
额外注意事项
- 确保你的PHP内存限制至少能容纳一个块的大小(比如100MB的话,内存限制设为128MB就够了)
- 每个块的大小必须至少5MB,B2不接受更小的块
- 上传过程中如果某个块失败,可以单独重新上传这个块,不用重新传整个文件
- 记得处理API请求的错误情况(比如网络中断、授权失败等)
内容的提问来源于stack exchange,提问作者Rohan
相关产品推荐
相关产品推荐

