storage::disk('oss')->put()上传后文件为空,主因是oss驱动未正确处理流资源或未关闭写入句柄;需确保上传前文件可读、显式rewind()或转为字符串,避免直接传tmp_name。

为什么 Storage::disk('oss')->put() 上传后文件是空的?
常见原因是 OSS 驱动未正确处理流资源或未关闭写入句柄。Laravel 默认 put() 接收字符串或资源,但部分 OSS SDK(如 aliyun/aliyun-oss-php-sdk)在流模式下需要显式 rewind() 或转为字符串,否则上传的是空内容。
实操建议:
- 上传前先检查文件是否可读:
fopen($path, 'rb')成功后再传给put() - 避免直接传
$_FILES['file']['tmp_name'],应使用file_get_contents()或fopen(..., 'rb')+stream_get_contents() - 若用
putFile(),确保表单字段名与request()->file()获取一致,且文件已通过isValid()校验
如何配置自定义 oss disk 并让 Storage 正确识别?
关键不在 config/filesystems.php 的 disks.oss 配置本身,而在于是否注册了对应驱动。Laravel 不会自动加载第三方驱动,必须手动绑定。
实操建议:
- 在
App\Providers\AppServiceProvider::boot()中调用:Storage::extend('oss', function ($app, $config) { return new \Illuminate\Filesystem\Cloud\OssAdapter($config); }); -
$config必须包含access_key、secret_key、bucket、endpoint、is_cname等字段,缺一不可 - 不要复用
local或s3的驱动类,OSS 必须用适配器(如overtrue/flysystem-oss提供的OssAdapter),否则签名或 URL 生成会失败
上传大文件时 timeout 或内存溢出怎么办?
默认 put() 是同步上传整个内容到内存,OSS SDK 对大文件支持分片上传(MultipartUpload),但 Laravel 的 put() 不触发该逻辑,需绕过 Storage 封装直接调用 SDK。
实操建议:
- 小于 5MB:继续用
Storage::disk('oss')->put($path, $content) - 大于 5MB:改用 SDK 原生分片上传,例如
$ossClient->uploadFile($bucket, $key, $localPath) - 若坚持走 Storage,可临时增大
memory_limit和max_execution_time,但不推荐用于生产 - 注意
oss配置中的timeout和connect_timeout必须设为 >60 秒,否则小文件也可能超时
生成带签名的临时访问 URL 总是 403?
根本原因:OSS 的签名 URL 依赖精确的 HTTP 方法、过期时间、Content-MD5、Content-Type 等参数,而 Storage::disk('oss')->temporaryUrl() 可能未按阿里云规范构造请求头或签名串。
实操建议:
- 优先使用 SDK 原生方法:
$ossClient->signUrl($bucket, $object, $expires) - 确认
temporaryUrl()的$expiration是 Unix 时间戳(非秒数差),且服务器时间与 NTP 同步,偏差超过 15 分钟即签名失效 - 如果用了 CNAME,请确保
endpoint配置为域名而非oss-cn-hangzhou.aliyuncs.com,否则签名 host 不匹配
真正麻烦的不是配置,而是 OSS 签名对时间精度和 header 顺序的苛刻要求——本地开发机时间不准、Docker 容器未挂载 NTP、测试环境用 localhost 做 endpoint,都可能让 URL 看似生成成功却 403。











