不能用mongofiles拉取S3文件,因其仅支持本地文件路径,不解析S3/HTTP协议,传URL会报错;必须用PyMongo+boto3流式下载并调用upload_from_stream()迁移。

直接用 mongofiles 无法拉取 S3 文件——它不支持 HTTP/S3 协议,只能读本地路径;必须用驱动代码(如 PyMongo)配合 boto3 流式下载 + GridFSBucket.upload_from_stream() 才能可靠迁移。
为什么不能用 mongofiles 拉 S3?
mongofiles 的 --file 参数只接受本地文件系统路径,传 S3 URL(如 s3://bucket/key 或 https://...)会报错:error: cannot open file: s3://...。它底层调用 fopen(),压根不解析 URI 协议。
- 试图用
wget或aws s3 cp中转再上传?对海量小文件来说,本地磁盘 I/O 和临时空间成瓶颈,且失败后难续传 - 用
mongofiles put加--filename伪装路径?没用——它仍要求本地存在该文件,只是改写 GridFS 中的filename字段 - S3 对象可能带特殊字符(如
+、?、非 ASCII),shell 展开或路径拼接时容易截断或编码错
PyMongo + boto3 流式迁移实操要点
核心是避免把整个对象 load 到内存:用 boto3.client.get_object() 返回的 StreamingBody 直接传给 GridFSBucket.upload_from_stream()。
- 必须显式指定
bucket_name(如"s3_imports"),否则默认写入fs,和业务 bucket 冲突 -
chunk_size_bytes建议设为256 * 1024(256KB):比默认 255KB 更规整,且兼容大多数 S3 分块策略 -
metadata至少包含{"s3_bucket": "my-bucket", "s3_key": "path/to/file.jpg", "last_modified": obj["LastModified"]},方便溯源 - 文件名用
os.path.basename(s3_key)提取,别直接用s3_key—— 否则 GridFS 的filename会含斜杠,查询时得用正则匹配
并发迁移时连接池和异常处理陷阱
多线程/多进程下,每个 worker 必须独占一个 MongoClient 实例,且需限制连接池大小;否则会出现 chunks 写入缺失、files 文档空挂、甚至 MongoDB 报 writeConcern error。
- 错误做法:全局复用一个
client = MongoClient(...),然后在线程里调bucket.upload_from_stream() - 正确做法:在每个线程函数内初始化
client = MongoClient(host, maxPoolSize=1),上传完立刻client.close() - 捕获两类关键异常:
botocore.exceptions.ClientError(S3 权限/不存在)、gridfs.errors.NoFile(上传中途断连导致 files 文档未落盘) - 失败后不要重试整个对象——先查
bucket.find_one({"metadata.s3_key": key}),存在就跳过;不存在再重试
校验迁移结果不能只看文件数
迁移脚本跑完,db.s3_imports.fs.files.countDocuments({}) 和 S3 对象数一致,不代表内容可靠:常见问题是 length 字段比实际字节数少几个字节,或 uploadDate 比 S3 的 LastModified 晚数秒——这说明流未完全 flush 或副本集同步延迟。
务必用 bucket.open_download_stream(file_id) 读出前 1KB 和后 1KB,与 S3 原对象对应位置做字节比对;大文件可抽样哈希(如 sha256)比对,而非全量校验。











