php用gridfsbucket接收php://input流上传文件时,必须fopen('php://input','rb')并fseek(0)重置指针,显式设filename和contenttype,调用uploadfromstream()前需禁用超时、调整chunksizebytes,并通过$options存metadata等元数据。

PHP用GridFSBucket接收php://input流上传文件
直接读php://input是唯一可靠方式——$_FILES在非multipart/form-data请求里为空,而前端用fetch()或XMLHttpRequest发二进制流时,默认就是application/octet-stream或自定义类型,根本不会触发$_FILES解析。
实操要点:
-
php://input只能读一次,且不能和$_POST/$_GET混用(会清空);务必先读流再处理其他参数 - 必须显式设置
stream_set_read_buffer($stream, 0),否则小文件可能卡住或截断 -
GridFSBucket::uploadFromStream()不支持直接传php://input资源句柄,得先fopen('php://input', 'rb')再fseek()重置指针(有些SAPI会默认把指针移到末尾) - 别漏掉
filename参数,它决定files.filename字段值,后续下载靠这个识别原始名
$bucket = new MongoDB\GridFS\GridFSBucket($database);
$stream = fopen('php://input', 'rb');
fseek($stream, 0); // 关键:确保从头读
$oid = $bucket->uploadFromStream(
$_POST['filename'] ?? 'unknown.bin',
$stream,
['contentType' => $_SERVER['CONTENT_TYPE'] ?? 'application/octet-stream']
);
fclose($stream);前端发的是FormData但后端没进$_FILES?检查Content-Type边界
常见现象:fetch()里用new FormData()追加文件,但PHP收不到$_FILES——八成是手动设置了Content-Type头。浏览器自动设置的multipart/form-data; boundary=xxx带边界标识,一旦手设Content-Type,边界就丢了,PHP无法解析分段,$_FILES自然为空。
正确做法:
立即学习“PHP免费学习笔记(深入)”;
- 前端
fetch()完全不设headers: {'Content-Type': ...},让浏览器自动生成带边界的Content-Type - 后端仍可用
$_FILES['file']['tmp_name']拿到临时路径,再用GridFSBucket::uploadFromPath()上传 - 若必须用流(比如大文件避免临时磁盘写入),就别碰
FormData,改用ArrayBuffer+Uint8Array构造纯二进制请求
uploadFromStream()卡住或报Broken pipe错误
本质是流读取和MongoDB写入节奏不匹配:PHP读太快,MongoDB还没准备好接收,或网络抖动导致连接中断。这不是代码写错,而是默认超时太短、缓冲区太小。
关键修复点:
- 调用前加
set_time_limit(0),避免脚本超时中断流读取 - 给
uploadFromStream()加'chunkSizeBytes' => 256 * 1024(256KB),比默认255KB更稳,减少分块次数 - 确认MongoDB连接配置里
'connectTimeoutMS' => 15000和'socketTimeoutMS' => 30000足够长 - 别用
php://memory或php://temp中转流——它们内存受限,大文件直接OOM
文件名、元数据、权限这些信息怎么存进GridFS
uploadFromStream()的第三个参数是$options数组,它直接映射到GridFS的files集合文档字段,不是随便塞的附加属性。
常用字段和坑点:
-
'metadata':存数组,比如['user_id' => 123, 'tags' => ['report', 'v2']]<code>,后续能用<code>find()按字段查 -
'contentType':影响HTTP响应头,但不会自动识别扩展名,得自己从$_FILES['file']['name']或前端传的content-type推导 -
'aliases':可存字符串数组,用于多别名查找,但别滥用——没有索引,查起来慢 - 别往
$options里塞资源句柄、闭包或大对象,序列化会失败,MongoDB驱动直接报Invalid argument
上传后拿到的$oid是MongoDB\BSON\ObjectId实例,不是字符串。要存数据库关联,得用(string) $oid转成字符串,否则下次查不到。











