MinIO 对象存储服务器,存储的文件变成了文件夹的问题

我在去年写了几篇使用 caddy + docker compose + minio 搭建自己维护的对象存储服务器的文章。最近两个月我更换了大概3台新的配置更高的服务器之后,镜像也随之更新为 minio-debian-12-2025 的 新的 docker 镜像了,然而几天前例行检查存储数据的时候发现了大问题:我用 MinIO 对象存储服务器 备份的数据库文件和程序文件都变成目录了,本来应该是 ***.zip 的文件,结果存在服务器上 ls -al 检查发现竟然是 d ,变成了目录。

服务器已经备份了近一个月的数据,视项目的大小和重要性不同每天分别自动备份1-2次。这下糟了,过去1个月的文件都白备份了,并且都是无用的备份,我当天发现时马上检查是不是可能是我的 docker compose 文件错了,仔细检查了好几遍没看出任何问题,并且新开了一个服务器实例,重新部署,还是一样的问题,折腾了好几个小时,还是一筹莫展,到半夜两点太晚就先睡了。

今天下午喝完咖啡突然想到问问 AI 大模型,我有 ChatGPT Pro, Claude 和 Gemini,分别问了它们一模一样的下面这个问题:

请帮我检查附件这个 docker compose 文件,这是一个 minio 对象存储实例,我已经成功运行,并且从客户端连接成功上传文件。但是刚发现有个错误:原始上传内容是一个文件,但是传到这个 minio 服务器就变成了一个文件夹,请帮我检查为什么会这样?以及如何修改?

然后附上我的 docker-compose 文件给它们。很快三个 AI 大模型都回答我了:

Claude 回答如下:(摘取主要片段)

我检查了您的 Docker Compose 文件,这个配置本身看起来没有问题。您所描述的问题(原始文件上传后变成文件夹)可能与 MinIO 的客户端配置或上传方式有关,而不是 Docker Compose 配置的问题。

这种情况通常可能是由以下几种原因造成的:

  1. 客户端上传配置问题:使用的客户端(SDK、命令行工具或web界面)在上传时的配置可能导致了这种行为。
  2. 对象键(Object Key)问题:如果对象键末尾包含斜杠(如 my-file/),MinIO 会将其视为文件夹而非文件。
  3. 多部分上传未完成:如果是使用分段上传方式,但上传未完成,可能会留下看起来像文件夹的中间状态。
  4. 位于嵌套路径中的文件:如果上传路径包含子目录结构(如 folder1/folder2/file.txt),MinIO会创建必要的目录结构。

 

Gemini 回答如下:(摘取主要片段)

你的 docker-compose.yml 文件看起来配置正确且相当标准。这个问题 几乎可以肯定不是由 Docker Compose 配置引起的,而是与你 如何上传文件 到 MinIO 有关。

在 S3 兼容的对象存储(如 MinIO)中,“文件夹”实际上是一个概念上的模拟。它通常通过以下两种方式实现:

  1. 零字节对象 + 结尾斜杠 (/): 创建一个大小为 0 字节的对象,其键名 (Object Key / Object Name) 以 / 结尾。例如,创建一个名为 myfolder/ 的对象,很多 S3 客户端或 MinIO Console 就会将其显示为一个名为 myfolder 的文件夹。

  2. 公共前缀 (Common Prefix): 如果你有多个对象,它们的键名共享相同的前缀并以 / 分隔(例如 myfolder/file1.txt 和 myfolder/file2.txt),那么 myfolder/ 就会被识别为一个逻辑上的文件夹,其中包含 file1.txt 和 file2.txt

你遇到的情况,最可能的原因是:

在上传文件时,你指定的 目标对象键名 (Object Key / Object Name) 错误地以 / 结尾了。

ChatGPT 回答如下:(摘取主要片段)

您遇到的问题是:上传到 MinIO 的文件在服务器上显示为文件夹而非单个文件。这个行为实际上是 MinIO 的正常设计结果,尤其是在使用 Erasure Coding(纠删码)存储模式时。

从 2022 年起,MinIO 默认采用 Erasure Coding 存储模式,即使在单节点部署中也是如此。在这种模式下,每个对象(文件)会被分割成多个数据块,并生成元数据文件(如 xl.meta)。这些数据块和元数据文件被存储在以对象名称命名的目录中。因此,在底层文件系统中,您会看到一个与上传文件同名的文件夹,其中包含这些分片和元数据文件。GitHub

这种设计旨在提高数据的可靠性和可扩展性。虽然在文件系统中看起来像是文件夹,但通过 MinIO 的 API 或客户端工具访问时,这些对象仍然表现为普通文件。

 

我最近因为在使用 Claude 和 Cursor 比较多,对 Claude 印象很好,所以最先看的 Claude,按照它给的提示再次检查和排错,一步一步跟 Claude 沟通,二十分钟后一切依旧,遂弃。来看 Gemini,Gemini 首先肯定了我的 compose 文件完全正确,排除了 compose 文件的问题,它认为可能是我的客户端设置不对,比如错误地使用了 / 斜杠,老实说我也怀疑过这个,但是我有超过30个备份程序在自动运行,不可能30个都写错了,而且我检查了几个链接,客户端的书写也是没问题的,再弃。

最后看到 ChatGPT,它的答案和前面 Claude 和 Gemini 完全不同,而且这个信息是我完全不知道的,会不会是在乱讲?我马上到服务器检查,到错误的目录中,本来应该是一个文件的“目录”中查看,看到了 xl.meta 文件,果然如它所言,有一种恍然醒悟的感觉。ChatGPT 说 MinIO 新的存储策略就是这样:每个对象(文件)会被分割成多个数据块,并生成元数据文件(如 xl.meta)。这些数据块和元数据文件被存储在以对象名称命名的目录中。因此,在服务器的文件系统中,我们会看到一个与上传文件同名的文件夹,其中包含这些分片和元数据文件。

我试着从客户端链接服务器取回数据,猜想如果取回的是文件,那么 ChatGPT 说的完全正确,如果取回的是目录,或者无法正确取回文件,那么 ChatGPT 就没有说对。很快就得到了验证,我取回的数据一切正常,没有任何问题。

感慨:ChatGPT 这个问题真是厉害!顺便记录下这个问题,用了 MinIO 这么久,却没关注到它这么大的一个变化。

 

 

发表回复