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_hash 是 project_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