不能直接用 echo.context 读取原始 multipart 流传 s3——php 在脚本执行前已解析并落地文件至 /tmp,$_files 的 tmp_name 是唯一可靠入口;标准做法是通过 $c.formfile() 获取 fileheader,再用 s3client::putobject() 配合 sourcefile 参数上传。

直接用 echo.Context 读取文件流再传给 S3 是行不通的——Echo 默认把上传文件写入临时磁盘,你拿到的只是 tmp_name 路径,不是原始字节流。想“绕过本地存储”本质是在对抗 PHP 底层机制,不推荐。
为什么不能直接读 multipart body 传 S3?
PHP 在脚本执行前就完成了 multipart 解析,并把文件落地到 /tmp;$_FILES 中的 tmp_name 是唯一可靠入口。试图用 $c.Request().Body 读原始流会失败或读到空/乱码,因为请求体已被消耗。
- 即使调用
$c.MultipartForm(),底层仍是基于$_FILES,不会改变临时文件行为 - 强行用
fopen('php://input')读原始流,会丢失文件名、类型等元信息,且无法处理多文件或混合字段 - 内存加载大文件(如 >50MB)极易触发
memory_limit错误或 OOM
标准做法:用 $_FILES + AWS SDK 上传
这是最稳、最符合 PHP 运行时实际的方式。Echo 只负责接收和校验,S3 上传交给 Aws\S3\S3Client::putObject()。
Echo框架 5.1.0 版本源码包下载,适合关注 RealIP 行为变化、StartConfig.Listener、NewDefaultFS 和观测性中间件入口的开发团队。
- 确保表单含
enctype="multipart/form-data" - 在 Echo handler 中用
$c.FormFile("file")获取*multipart.FileHeader,它封装了tmp_name和元数据 - 调用
$s3Client->putObject([ 'Bucket' => 'my-bucket', 'Key' => 'uploads/' . $header->Filename, 'SourceFile' => $header->TemporaryName, 'ACL' => 'public-read' ])
- 注意:不要用
upload_fileobj() 再读一遍 <code>TemporaryName文件句柄——SDK 的SourceFile参数已隐式完成读取
大文件或 PaaS 环境该怎么做?
当 /tmp 空间受限(如 Heroku、Cloud Run),或并发上传量大时,别硬扛本地磁盘。应切换为浏览器直传:
- 后端用 SDK 生成预签名 POST 表单策略(
$s3Client->createPresignedPost()),返回给前端 - 前端用
fetch或XMLHttpRequest直连 S3,绕过你的服务器 - Echo 只需提供一个签发策略的 endpoint,不碰文件内容
- 额外工作:需在 S3 Bucket Policy 中允许该 POST 请求,并校验回调或监听 S3 Event 触发后续业务逻辑
真正容易被忽略的是:S3 的 ACL 和 Content-Type 必须显式设置。用 SourceFile 上传时,SDK 不会自动从文件扩展名推断 MIME 类型,若漏设 'ContentType' => $header->Header.Get("Content-Type"),图片可能变成下载而非展示。










