为何Oneup/UploaderBundle上传1GB文件慢?Symfony与纯PHP差异解析
问题分析与优化方案
为什么纯PHP脚本更快?
核心原因有三点:
- 上传机制差异
AWS SDK对SourceFile和Body参数的处理逻辑完全不同:- 使用
SourceFile时,SDK会自动检测文件大小,默认超过5MB就触发分块上传(Multipart Upload),可并行传输多个文件块,大幅提升大文件上传速度。 - 使用
Body传递fopen资源流时,默认是单块上传,1GB文件单块传输不仅效率低,还容易受网络波动影响,导致耗时翻倍。
- 使用
- Symfony框架的额外开销
控制器运行在完整的Symfony环境中,包含服务容器初始化、事件监听、日志记录等流程,对比纯PHP的极简环境,会增加少量耗时。 - Bundle封装的额外处理
Oneup/UploaderBundle + FlysystemBundle的双层封装,会增加文件接收、格式验证、适配器转换等步骤,进一步拉长上传时间。
优化上传速度的方案
1. 改用SourceFile替代资源流
在Symfony控制器中,直接传递文件路径给SourceFile参数,让SDK自动启用分块上传:
$pathToFile = $this->getParameter('kernel.project_dir') . '/public/big.bin'; $result = $client->putObject([ 'Bucket' => 'xxxtest', 'Key' => 'big.bin', 'SourceFile' => $pathToFile // 替换Body为SourceFile ]);
2. 配置Flysystem自动分块上传
给Oneup Flysystem的S3适配器添加分块参数,让Flysystem自动使用分块上传大文件:
# oneup_flysystem.yaml oneup_flysystem: adapters: xxx_fr_sync.flysystem_adapter: awss3v3: client: xxx_fr.sync_s3_client bucket: 'xxxtest' prefix: '' options: multipart_threshold: 5242880 # 超过5MB自动分块 multipart_chunk_size: 10485760 # 每块10MB(可根据网络调整)
3. 手动配置分块上传(针对资源流场景)
如果必须使用资源流上传,手动开启分块上传,示例代码:
$pathToFile = $this->getParameter('kernel.project_dir') . '/public/big.bin'; $resource = fopen($pathToFile, 'r'); // 初始化分块上传 $uploadId = $client->createMultipartUpload([ 'Bucket' => 'xxxtest', 'Key' => 'big.bin' ])['UploadId']; $parts = []; $partNumber = 1; $chunkSize = 10 * 1024 * 1024; // 10MB每块 while (!feof($resource)) { $chunk = fread($resource, $chunkSize); $result = $client->uploadPart([ 'Bucket' => 'xxxtest', 'Key' => 'big.bin', 'UploadId' => $uploadId, 'PartNumber' => $partNumber, 'Body' => $chunk ]); $parts[] = [ 'PartNumber' => $partNumber, 'ETag' => $result['ETag'] ]; $partNumber++; } // 完成分块上传 $client->completeMultipartUpload([ 'Bucket' => 'xxxtest', 'Key' => 'big.bin', 'UploadId' => $uploadId, 'MultipartUpload' => ['Parts' => $parts] ]); fclose($resource);
4. 修复异步上传报错
之前异步上传出现“Could not contact remote server”,大概率是缺少异步HTTP客户端依赖或配置错误,按以下步骤修复:
- 安装依赖:
composer require guzzlehttp/guzzle guzzlehttp/psr7 react/event-loop
- 配置异步HTTP客户端:
use Aws\S3\S3Client; use GuzzleHttp\Client as GuzzleClient; use GuzzleHttp\HandlerStack; use GuzzleHttp\Handler\CurlMultiHandler; // 创建异步处理栈 $handler = new CurlMultiHandler(); $stack = HandlerStack::create($handler); $guzzleClient = new GuzzleClient(['handler' => $stack]); // 初始化带异步客户端的S3Client $client = new S3Client([ 'region' => 'fr-par', 'endpoint' => 'https://s3.fr-par.scw.cloud', 'version' => 'latest', 'credentials' => [ 'key' => 'publickey', 'secret' => 'secretkey' ], 'http_handler' => $guzzleClient ]); // 异步上传 $promise = $client->putObjectAsync([ 'Bucket' => 'xxxtest', 'Key' => 'big.bin', 'SourceFile' => $pathToFile ]); // 等待上传完成 $promise->wait();
5. 优化Symfony环境开销
- 给上传路由单独配置,禁用不必要的中间件和事件监听,比如在
routes.yaml中添加:
upload_big_file: path: /upload/big controller: App\Controller\MainController::upload defaults: _controller: App\Controller\MainController::upload requirements: _method: POST options: expose: true # 排除不需要的事件,比如 profiler、logger _event_listener: - kernel.event_listener.controller_args_resolver: ~ - kernel.event_listener.security: ~
- 关闭Symfony调试模式(生产环境默认关闭),调试模式会增加大量日志和性能监控开销。
内容的提问来源于stack exchange,提问作者heryforce
相关产品推荐
相关产品推荐

