Help us learn about your current experience with the documentation. Take the survey.

GitLab 中的文件存储

我们使用 CarrierWave gem 来处理文件的上传、存储和检索。

文件上传应该通过 workhorse 加速,详情请参考 uploads 开发文档

根据上下文,文件上传在许多地方都有使用:

  • 系统
    • 实例 Logo(登录/注册页面可见的 logo)
    • Header Logo(导航栏中显示的 Header Logo)
  • 群组
    • 群组头像
  • 用户
    • 用户头像
    • 用户代码片段附件
  • 项目
    • 项目头像
    • 问题/合并请求/笔记的 Markdown 附件
    • 问题/合并请求/笔记的旧版 Markdown 附件
    • CI 工件(归档、元数据和跟踪信息)
    • LFS 对象
    • 合并请求差异
    • 设计管理设计缩略图
  • 主题
    • 主题头像

磁盘存储

GitLab 最初将所有内容保存在本地磁盘上。虽然目录位置相比之前的版本有所变化,但它们仍然没有完全标准化。您可以在下面看到它们:

描述 在数据库中? 相对路径(从 CarrierWave.root 开始) 上传器类 模型类型
实例 logo yes uploads/-/system/appearance/logo/:id/:filename AttachmentUploader Appearance
Header logo yes uploads/-/system/appearance/header_logo/:id/:filename AttachmentUploader Appearance
群组头像 yes uploads/-/system/group/avatar/:id/:filename AvatarUploader Group
用户头像 yes uploads/-/system/user/avatar/:id/:filename AvatarUploader User
用户代码片段附件 yes uploads/-/system/personal_snippet/:id/:random_hex/:filename PersonalFileUploader Snippet
项目头像 yes uploads/-/system/project/avatar/:id/:filename AvatarUploader Project
主题头像 yes uploads/-/system/projects/topic/avatar/:id/:filename AvatarUploader Topic
问题/合并请求/笔记的 Markdown 附件 yes uploads/:hash_project_id/:random_hex/:filename FileUploader Project
设计管理设计缩略图 yes uploads/-/system/design_management/action/image_v432x230/:id/:filename DesignManagement::DesignV432x230Uploader DesignManagement::Action
CI 工件(CE) yes shared/artifacts/:disk_hash[0..1]/:disk_hash[2..3]/:disk_hash/:year_:month_:date/:job_id/:job_artifact_id (:disk_hashproject_id 的 SHA256 摘要) JobArtifactUploader Ci::JobArtifact
LFS 对象(CE) yes shared/lfs-objects/:hex/:hex/:object_hash LfsObjectUploader LfsObject
外部合并请求差异 yes shared/external-diffs/merge_request_diffs/mr-:parent_id/diff-:id ExternalDiffUploader MergeRequestDiff
可度量问题图片 yes uploads/-/system/issuable_metric_image/file/:id/:filename IssuableMetricImageUploader IssuableMetricImage

CI 工件和 LFS 对象在 CE 和 EE 中的行为不同。在 CE 中,它们继承 GitlabUploader,而在 EE 中,它们继承 ObjectStorage 并将文件存储在与 S3 API 兼容的对象存储中。

Markdown 中问题、合并请求(MR)和笔记的附件使用 哈希存储,使用项目 ID 的哈希值。

我们提供了一个 一站式 Rake 任务,可以一次性将所有上传内容迁移到对象存储。如果引入了新的上传器类或模型类型,请确保将其对应的 Rake 任务调用添加到 类别列表 中。

路径段

文件存储在多个位置,使用不同的路径方案。所有 GitlabUploader 的派生类都应遵循此路径段模式:

|   GitlabUploader
| ----------------------- + ------------------------- + --------------------------------- + -------------------------------- |
| `<gitlab_root>/public/` | `uploads/-/system/`       | `user/avatar/:id/`                | `:filename`                      |
| ----------------------- + ------------------------- + --------------------------------- + -------------------------------- |
| `CarrierWave.root`      | `GitlabUploader.base_dir` | `GitlabUploader#dynamic_segment`  | `CarrierWave::Uploader#filename` |
|                         | `CarrierWave::Uploader#store_dir`                             |                                  |

|   FileUploader
| ----------------------- + ------------------------- + --------------------------------- + -------------------------------- |
| `<gitlab_root>/shared/` | `artifacts/`              | `:year_:month/:id`                | `:filename`                      |
| `<gitlab_root>/shared/` | `snippets/`               | `:secret/`                        | `:filename`                      |
| ----------------------- + ------------------------- + --------------------------------- + -------------------------------- |
| `CarrierWave.root`      | `GitlabUploader.base_dir` | `GitlabUploader#dynamic_segment`  | `CarrierWave::Uploader#filename` |
|                         | `CarrierWave::Uploader#store_dir`                             |                                  |
|                         |                           | `FileUploader#upload_path`                                           |

|   ObjectStore::Concern (store = remote)
| ----------------------- + ------------------------- + ----------------------------------- + -------------------------------- |
| `<bucket_name>`         | <ignored>                 | `user/avatar/:id/`                  | `:filename`                      |
| ----------------------- + ------------------------- + ----------------------------------- + -------------------------------- |
| `#fog_dir`              | `GitlabUploader.base_dir` | `GitlabUploader#dynamic_segment`    | `CarrierWave::Uploader#filename` |
|                         |                           | `ObjectStorage::Concern#store_dir`  |                                  |
|                         |                           | `ObjectStorage::Concern#upload_path`                                   |

RecordsUploads::Concern concern 会为每个由 GitlabUploader 存储的文件创建一个 Upload 条目,使用 GitlabUploader#dynamic_path 持久化路径的动态部分。然后您可以使用 Upload#build_uploader 方法来操作文件。

对象存储

通过在 GitlabUploader 的派生类中包含 ObjectStorage::Concern,您可以为该上传器启用对象存储。要在您的上传器中启用对象存储,您需要:1) 包含 RecordsUpload::Concern 并前置 ObjectStorage::Extension::RecordsUploads,或者 2) 挂载上传器并创建一个名为 <mount>_store 的新字段。

CarrierWave::Uploader#store_dir 被重写为

  • 当存储为 LOCAL 时:GitlabUploader.base_dir + GitlabUploader.dynamic_segment
  • 当存储为 REMOTE 时:GitlabUploader.dynamic_segment(使用存储桶名称进行命名空间)

使用 ObjectStorage::Extension::RecordsUploads

如果尚未包含,此 concern 会包含 RecordsUploads::Concern

ObjectStorage::Concern 上传器会搜索匹配的 Upload 以选择正确的对象存储。Upload 使用 #store_dirs + identifier 为每个存储(LOCAL/REMOTE)进行映射。

class SongUploader < GitlabUploader
  include RecordsUploads::Concern
  include ObjectStorage::Concern
  prepend ObjectStorage::Extension::RecordsUploads

  ...
end

class Thing < ActiveRecord::Base
  mount :theme, SongUploader # 我们有一首很棒的主题歌!

  ...
end

使用挂载的上传器

ObjectStorage::Concern 查询 model.<mount>_store 属性以选择正确的对象存储。此列必须存在于模型架构中。

class SongUploader < GitlabUploader
  include ObjectStorage::Concern

  ...
end

class Thing < ActiveRecord::Base
  attr_reader :theme_store # 这是一个 ActiveRecord 属性
  mount :theme, SongUploader # 我们有一首很棒的主题歌!

  def theme_store
    super || ObjectStorage::Store::LOCAL
  end

  ...
end