Label Studio 外部存储接入实战:云存储与本地目录的同步、权限与安全配置指南 📅 发布时间:2026/9/12 23:57:47 👁 浏览次数: Label Studio 外部存储接入实战云存储与本地目录的同步、权限与安全配置指南【免费下载链接】label-studioLabel Studio is a multi-type data labeling and annotation tool with standardized output format项目地址: https://gitcode.com/GitHub_Trending/la/label-studioLabel Studio 允许将 Amazon S3、Google Cloud Storage、Microsoft Azure Blob、Redis 数据库及本地文件目录接入项目自动采集桶、容器、数据库或目录中的新数据并把标注结果回写从而无缝嵌入机器学习流水线。本文以 storage.md 为骨架结合label_studio/io_storages模块源码系统讲解源存储Source Storage与目标存储Target Storage的同步机制、导入方式、预签名 URL 与代理存储两种取数链路以及权限与排障实践。支持的存储类型与版本差异Label Studio 将外部存储抽象为源存储从外部拉取任务与目标存储把标注结果写回外部两种连接。两者都可以在一个项目中同时存在每个存储连接都是项目级project-specific的你可以为一个项目挂载多个桶、容器、数据库或目录。下表来自 storage.md存储Community开源版Enterprise企业版Amazon S3✅✅Amazon S3 with IAM role❌✅Google Cloud Storage✅✅Google Cloud Storage WIF Auth❌✅Google Cloud Storage with service account impersonation for GKE❌✅Microsoft Azure Blob Storage✅✅Microsoft Azure Blob Storage with Service Principal❌✅Databricks Files (UC Volumes)❌✅Databricks Files (UC Volumes) with Service Principal❌✅Redis database✅✅Local storage✅✅从源码结构看各存储类型在 label_studio/io_storages/ 下按s3/、gcs/、azure_blob/、redis/、localfiles/分目录实现每个目录都包含api.py、models.py、serializers.py、urls.py与form_layout.yml。函数 get_storage_list() 中注册了s3、gcs、azure、redis四种云存储的导入/导出 API本地文件存储Local Files与 Databricks企业版则作为独立连接类型存在。模型 Storage 通过url_scheme如s3://、gs://、azure-blob://、redis://标识存储类型并在 can_resolve_scheme 中校验 URL 前缀与桶/容器/路径是否匹配该存储连接这是 URI 解析的基础。源存储Source Storage源存储连接用于把外部数据同步成标注任务。需要注意Label Studio 不会自动同步源存储中的新数据。当你向已连接的桶上传新文件后需要在 UI 中手动点击 Sync 触发同步也可以调用 Label Studio API 创建或同步存储连接。从云端同步来的任务数据不会持久化存储到 Label Studio 数据库中任务字段里保存的是指向存储对象的 URI如s3://bucket/1.jpg标注界面通过预签名 URLpresigned URL按需加载媒体。你还可以结合 VPC 与 IP 限制来加固对云存储的访问见 security.md 中 Secure access to cloud storages 一节。源存储权限要求源存储所需的权限取决于导入方式导入方式为Files后端只需LIST权限不会从桶中下载任何数据导入方式为Tasks后端需要GET权限读取 JSON 文件并转换为 Label Studio 任务。当标注用户访问任务时后端会把s3://之类的 URI 解析成 HTTPS 链接返回给前端由用户浏览器直接加载。浏览器加载媒体需要云存储授予HEAD 与 GET权限HEAD 请求发生在加载初期用于确定音频、视频等文件的体积随后浏览器发起 GET 请求拉取文件正文。同步与 URI 解析两个环节源存储功能在 ImportStorage 中被拆成两个阶段Sync同步Label Studio 扫描存储中的对象并把新对象导入为任务URI resolvingURI 解析后端请求云存储把s3://bucket/1.jpg解析为 HTTPS 链接以便浏览器加载媒体。同步流程的底层实现位于 ImportStorage.sync() 与 _scan_and_create_links()有 Redis 时任务被投递到low优先级的 RQ 队列import_sync_background无 Redis 时则同步执行。扫描过程中对每个新 key 调用get_data()读取内容、通过add_task()创建任务与ImportStorageLink关联记录并在结束时回写StorageInfo状态。URI 解析对应resolve_uris()方法base_models.py它递归处理字符串、列表、字典中的每个s3://等前缀 URI替换为指向storages:task-storage-data-resolve端点的代理地址urls.py。StorageInfobase_models.py负责记录每次同步的状态机initialized → queued → in_progress → completed / completed_with_errors / failed、上次同步时间、同步数量、错误堆栈并通过 Redis 任务心跳做健康检查超时任务会被标记为 failed。导入方式Import method源存储在 UI 上提供Import method下拉框原 Treat every bucket object as a source file 选项已重命名合并进来决定任务以何种方式装载Tasks按任务文件导入设置为Tasks时JSON、JSONL/NDJSON 或 Parquet 格式的任务文件可以直接从存储桶加载进 Label Studio。这种方式特别适合单个任务包含多个媒体源的复杂场景。可以在同一个 JSON 文件中放多个任务但不能在同一文件中混用不同的任务格式。裸任务bare tasks示例task_01.json{ image: s3://bucket/1.jpg, text: opossums are awesome }也可以在一个数组中放多个任务tasks.json[ { image: s3://bucket/1.jpg, text: opossums are awesome }, { image: s3://bucket/2.jpg, text: cats are awesome } ]带标注与预测的任务示例task_with_predictions_and_annotations_01.json{ data: { image: s3://bucket/1.jpg, text: opossums are awesome }, annotations: [...], predictions: [...] }多个任务合并到一个数组文件tasks_with_predictions_and_annotations.json[ { data: { image: s3://bucket/1.jpg, text: opossums are awesome }, annotations: [...], predictions: [...] }, { data: { image: s3://bucket/2.jpg, text: cats are awesome }, annotations: [...], predictions: [...] } ]JSONL 逐行任务示例tasks.jsonl{ image: s3://bucket/1.jpg, text: opossums are awesome } { image: s3://bucket/2.jpg, text: cats are awesome }在 Label Studio Enterprise 与 Starter Cloud 版本中Parquet 文件也可以像 JSON/JSONL 一样导入任务。从源码看任务导入由 ImportStorage.add_task() 完成它解析predictions/annotations字段两者出现时任务必须包含data字段、创建Task记录、通过PredictionSerializer与AnnotationSerializer校验并写入预测与标注最后回填任务的total_annotations、total_predictions等计数并触发 webhookWebhookAction.TASKS_CREATED。ImportStorageLink模型base_models.py记录任务与存储 key 的一一对应关系并为 Parquet 行保留row_group/row_index。Files按文件对象导入设置为Files时Label Studio 自动列出存储桶中的文件并为每个文件构造一个任务。这种方式只适用于单个媒体源的简单标注任务如一张图片、一段文本等。同步时若检测到非 JSON 文件base_models.py 会抛出UnsupportedFileFormatError并提示改为启用 Tasks 导入方式。预签名 URL 与存储代理Pre-signed URLs vs Storage proxiesLabel Studio 从云存储拉取媒体有两种安全机制取决于创建源存储时Use pre-signed URLs开关的状态默认开启Use pre-signed URLs 开启走预签名 URL 模式Use pre-signed URLs 关闭走存储代理Proxy storage模式媒体流量经后端转发。在企业版中组织管理员可在Organization Usage License页面勾选Enable Storage Proxy控制组织是否允许使用存储代理需 Owner 角色操作。当Enable Storage Proxy被禁用时组织内用户将无法创建或修改关闭了 Presigned URLs 的源存储连接从而强制所有连接使用预签名 URL。预签名 URL 模式此模式下浏览器收到HTTP 303 重定向跳转到带时效的 S3/GCS/Azure 预签名 URL这是默认行为。它的核心收益是让媒体文件尽可能隔离于 Label Studio 网络浏览器直接从云存储下载不经过 Label Studio 服务器。所需权限已包含在各云存储配置文档中。源码中 resolve_s3_url() 调用 boto3 的generate_presigned_url(ClientMethodget_object, ExpiresInexpires_in)生成链接proxy_api.py 中的redirect_to_presign_url()生成 303 重定向响应TTL 取自存储的presign_ttl默认 1 分钟见 s3/models.py。存储代理模式代理模式下Label Studio 后端在服务端拉取对象并流式转发给浏览器媒体不经过用户浏览器直连云存储。其收益包括安全媒体访问进一步受 Label Studio 用户角色与项目访问权限约束该访问控制同样作用于缓存文件——即使媒体被缓存若用户对任务的访问被撤销该文件的访问也会被限制数据始终留在 Label Studio 网络边界内对希望单点收敛网络流量的本地化on-prem环境尤其有用。配置无需配置 CORS无需预签名相关权限。从源码看代理模式由 proxy_api.py 的流式响应实现TaskResolveStorageUri视图proxy_api.py在storage.presignFalse时通过get_bytes_stream()从存储取流并透传 Range 请求头支持分片续传与Accept-Ranges: bytes响应S3 实现见 s3/models.py直接把 Range 头转发给 S3 并返回原始流。代理模式需要授予的权限AWS S3{ Version: 2012-10-17, Statement: [ { Effect: Allow, Action: [ s3:GetObject, s3:ListBucket ], Resource: [ arn:aws:s3:::your-bucket-name, arn:aws:s3:::your-bucket-name/* ] } ] }Google Cloud Storagestorage.objects.get读取对象数据与元数据、storage.objects.list若使用前缀列出桶内对象Azure Blob Storage添加Storage Blob Data Reader角色包含Microsoft.Storage/storageAccounts/blobServices/containers/blobs/read与Microsoft.Storage/storageAccounts/blobServices/containers/blobs/getTags/action。本地化on-prem部署注意事项大媒体文件按 8 MB 顺序分块流式传输每块是一个独立的 GET 请求这可能导致频繁请求后端获取下一段数据、消耗额外资源。可通过以下环境变量调整配置位置见 base.pyRESOLVER_PROXY_MAX_RANGE_SIZE默认 8 MB定义每次请求返回的最大块大小RESOLVER_PROXY_TIMEOUT默认 20 秒定义 uWSGI worker 处理单个请求的最大时长。相关源码常量还包括RESOLVER_PROXY_BUFFER_SIZE默认 512 KB、RESOLVER_PROXY_ENABLE_ETAG_CACHE默认开启与RESOLVER_PROXY_CACHE_TIMEOUT默认 3600 秒用于控制代理流式传输的缓冲与缓存行为。目标存储Target Storage配置目标存储后标注结果会同时保存到 Label Studio 数据库与目标存储两处标注者在提交或更新任务点击Submit/Update时标注结果除写入数据库外也会推送到目标存储用户在目标存储上点击Sync按钮时所有标注会从数据库重新完整导出一遍。目标存储接收到的是每个标注的 JSON 格式导出结果导出结构详见 export.md 中 Label Studio JSON format of annotated tasks 一节。你还可以开启删除同步选项默认关闭当标注在 Label Studio 中被删除时目标存储中的对应标注也会被删除。目标存储权限使用目标存储必须具有PUT权限DELETE权限为可选仅在开启上述删除同步时必需。目标存储的底层实现从源码看目标存储由 ExportStorage 实现save_annotations()使用最多 8 个线程的线程池max_workers取min(8, cpu_count * 4)分批导出标注save_all_annotations()导出项目全部标注save_only_new_annotations()仅补齐缺失的ExportStorageLink关联而不重写已有标注sync()方法与源存储一致有 Redis 时走low队列后台任务否则同步执行。ExportStorageLinkbase_models.py记录标注与目标对象的关联默认以标注 ID 命名导出对象get_key()返回str(annotation.id)从而避免重复导出。以 Amazon S3 为例的完整配置流程以下步骤来自 storage_s3.md演示如何为 S3 配置源/目标存储。1. 配置桶访问权限假设使用同一个 AWS 角色同时管理源存储与目标存储。如果只用 S3 做源存储则无需PUT权限。启用编程访问参照 Amazon Boto3 配置文档设置访问凭证。注意会话令牌Session Token仅在临时安全凭证场景下需要。分配角色策略把your_bucket_name替换为真实桶名{ Version: 2012-10-17, Statement: [ { Sid: VisualEditor1, Effect: Allow, Action: [ s3:ListBucket, s3:GetObject, s3:PutObject, s3:DeleteObject ], Resource: [ arn:aws:s3:::your_bucket_name, arn:aws:s3:::your_bucket_name/* ] } ] }其中s3:PutObject仅目标存储需要s3:DeleteObject仅当希望在 Label Studio 删除标注时同步删除桶内对象才需要。配置 CORS为桶配置跨域资源共享策略允许从 Label Studio 部署的同一主机名发起 GET 请求。仅在使用预签名 URL 时需要使用代理模式则无需配置 CORS。示例[ { AllowedHeaders: [ * ], AllowedMethods: [ GET ], AllowedOrigins: [ * ], ExposeHeaders: [ x-amz-server-side-encryption, x-amz-request-id, x-amz-id-2 ], MaxAgeSeconds: 3000 } ]2. 创建源存储连接在项目中进入Settings Cloud Storage Add Source Storage选择Amazon S3并点击 Next。Configure Connection连接配置各字段字段说明Storage Title存储连接名称Bucket NameS3 桶名称Region NameAWS 区域名例如us-east-1S3 Endpoint可选自定义 S3 端点用于覆盖 S3 默认生成的访问 URLAccess Key ID具备桶访问权限的 AWS 账号访问密钥 IDSecret Access Key上述账号的密钥Session Token可选临时安全凭证的会话令牌Use pre-signed URLsOn/ Proxy through the platformOff决定数据加载方式开启则生成带时效的 HTTPS 链接并以 303 重定向让浏览器直连云存储速度快、扩展性好但要求桶具备正确的 CORS 与预签名权限关闭则由后端从云存储下载并流式转发给浏览器媒体流量全部经过 Label Studio数据保持在网络边界内、每次请求都做任务级访问检查且无需 CORS/预签名配置但更消耗 worker 资源、速度略慢Expire pre-signed URLs (minutes)预签名 URL 的有效期控制填写后点击Test connection验证连接。对应实现中S3 客户端按access_key_id:secret:token:region:endpoint缓存s3/models.pyvalidate_connection()在有前缀时执行list_objects_v2、无前缀时执行head_bucket检查s3/models.py。Import Settings Preview导入设置与预览各字段字段说明Bucket Prefix可选桶内目录名例如data-set-1或data-set-1/subfolder-2Import Method选择按桶内每个文件创建任务Files或用 JSON/JSONL/Parquet 文件定义每个任务的数据TasksFile Name Filter正则表达式过滤桶对象用.*收集所有对象Scan all sub-folders开启后递归扫描桶内所有子目录点击Load preview确认同步的数据正确。Review Confirm阶段点击Save Sync立即同步或Save仅保存设置稍后同步。也可通过 API 调用同步导入存储。3. 创建目标存储连接在项目中进入Settings Cloud Storage Add Target Storage选择Amazon S3并点击 Next。字段除上述连接参数外还包含字段说明Bucket Prefix可选目标目录例如data-set-1或data-set-1/subfolder-2Can delete objects from storage开启后Label Studio 中删除标注时同步删除 S3 桶中的对应对象存储凭证必须具备删除权限添加完成后点击Sync触发导出也可通过 API 同步导出存储。Amazon S3 with IAM role企业版企业版支持用带 External ID 的 IAM 角色替代 Access Key/Secret提供可撤销的临时安全凭证访问桶。设置要点在 Label Studio 的Organization页面获取组织External ID需 Owner/Admin 权限在 AWS 创建 IAM 角色设置时要求 External ID、不要求多因素认证MFA用 External ID 创建信任策略示例{ Version: 2012-10-17, Statement: [ { Effect: Allow, Principal: { AWS: [ arn:aws:iam::490065312183:role/label-studio-app-production ] }, Action: sts:AssumeRole, Condition: { StringEquals: { sts:ExternalId: [ YOUR-ORG-ExternalId ] } } } ] }记录角色 ARN配置源存储时填入Role ARN字段为角色分配策略源存储只需s3:ListBucket与s3:GetObject目标存储需要s3:ListBucket、s3:PutObject、s3:GetObject删除同步时再加s3:DeleteObject。注意Label Studio Cloud 用户若在 2025 年 4 月 7 日之前创建的 IAM 角色策略未包含新 principalarn:aws:iam::490065312183:role/label-studio-app-production在新建连接前需要将其加入 principal 列表旧 principal 需保留以便存量项目继续加载数据。本地文件存储Local Storage本地存储允许把 Label Studio 运行机器上的指定目录作为源或目标存储仅适用于本地化部署云版app.humansignal.com不支持Label Studio 会递归扫描目录读取任务。从本地文件系统提供数据存在安全风险需自行评估。详细步骤见 storage_local.md。前置环境变量LABEL_STUDIO_LOCAL_FILES_SERVING_ENABLEDtrueLABEL_STUDIO_LOCAL_FILES_DOCUMENT_ROOT指向本机目录必须是你数据所在文件夹的父目录。例如 Mac/Unix 下目录结构为/home/user/ls-demo/内含my-data/与audio/两个子目录时export LABEL_STUDIO_LOCAL_FILES_SERVING_ENABLEDtrue export LABEL_STUDIO_LOCAL_FILES_DOCUMENT_ROOT/home/user此时 UI 中Absolute local path填/home/user/ls-demo可引用两个子目录填/home/user/ls-demo/my-data则只引用其中一个。Windows 下路径使用双反斜杠例如LABEL_STUDIO_LOCAL_FILES_DOCUMENT_ROOTC:\\Users\\YourName\\Documents。创建源/目标存储连接源存储连接在Settings Cloud Storage Add Source Storage中选择Local Files字段包括 Storage Title、Absolute local path预填LABEL_STUDIO_LOCAL_FILES_DOCUMENT_ROOT值可追加子目录、Import MethodFiles / Tasks、File Name Filter正则过滤.*收集全部、Scan all sub-folders递归扫描。目标存储连接类似额外提供Can delete objects from storage选项。多数据源任务的本地引用格式当标注配置包含多个对象标签如一个任务同时含音频与文本、两个音频或图片与文本时必须用 JSON/JSONL/Parquet 定义任务Parquet 仅企业版支持并注意源存储指向所有数据文件所在目录Import Method设为TasksFile Name Filter留空在Review Confirm阶段点Save而非Save Sync避免为目录中每个文件自动创建任务。任务文件中引用本地文件使用格式/data/local-files/?dpath/file。单文件夹示例数据都在my-data绝对路径追加my-data[{ data: { image: /data/local-files/?dmy-data/image1.png, text: /data/local-files/?dmy-data/text1.txt } }, { data: { image: /data/local-files/?dmy-data/image2.png, text: /data/local-files/?dmy-data/text2.txt } }]跨文件夹示例同时引用my-data与audio绝对路径为/home/user/ls-demo引用时需带上ls-demo前缀[{ data: { image: /data/local-files/?dls-demo/my-data/image1.png, text: /data/local-files/?dls-demo/my-data/text1.txt, audio: /data/local-files/?dls-demo/audio/audio1.mp3 } }, { data: { image: /data/local-files/?dls-demo/my-data/image2.png, text: /data/local-files/?dls-demo/my-data/text2.txt, audio: /data/local-files/?dls-demo/audio/audio2.mp3 } }]任务文件可以通过 UI 上传、调用 API 导入、放入本地存储后用文件名过滤只同步 JSON/JSONL/Parquet 文件或另建一个源存储连接存放任务文件。Docker 下的本地存储使用 Docker 运行时需要把本地目录以卷volume方式挂载进容器应用运行于/label-studio。社区版有自动检测逻辑当LABEL_STUDIO_LOCAL_FILES_DOCUMENT_ROOT与LABEL_STUDIO_LOCAL_FILES_SERVING_ENABLED未设置时Label Studio 自动在当前工作目录查找名为mydata或label-studio-data的文件夹相关自动配置逻辑见 base.py因此把宿主机目录挂载到/label-studio/mydata或/label-studio/label-studio-data即可免额外配置启用本地文件服务。同步过程中的状态与任务去重从 StorageInfo 的实现可以看到每次同步都会经历initialized → queued → in_progress → completed或 completed_with_errors / failed的状态流转并记录last_sync、last_sync_count、last_sync_job与traceback。_scan_and_create_links()在扫描时通过ImportStorageLink.exists()对 key 去重已同步过的对象不会被重复导入任务创建成功后还会通过backfill_fsm_states_for_tasks补齐任务状态机并通过 webhook按WEBHOOK_BATCH_SIZE批量通知任务创建事件。因此重复点击 Sync 不会产生重复任务这是同步幂等的源码级保证。排障与最佳实践常见注意点源存储Files 方式Label Studio 并不真正导入桶中数据而是创建对对象的引用因此你对同步到标注界面的数据拥有完整访问控制权源存储Tasks 方式桶内文件被视为不可变将更新后的文件推送到 Label Studio 的唯一办法是换一个新文件名上传或删除与该文件关联的所有任务后重新同步与外部桶的同步单向进行源存储从桶创建任务、目标存储把标注推回桶。修改桶侧内容不保证结果一致性建议每个 Label Studio 项目使用独立的桶目录存储区域选择为降低延迟、提升效率应将数据存放在地理上更接近标注团队而非 Label Studio 服务器的云存储桶中。社区版更多排障信息可参考 troubleshooting.md企业版可查阅 HumanSignal 支持中心的 Import, Export Storage 专区。结合源码判断同步问题同步卡在in_progress超过STORAGE_IN_PROGRESS_TIMER默认 5 秒base.py的 5 倍且无心跳时health_check()会把存储状态置为 failed报错last ping time is too old通常意味着后台 worker 被意外重载或任务被手动移除Redis 中找不到last_sync_job对应任务时同样会标记 failed常见于 OOM 或重部署场景此时应检查 RQ worker 状态与内存配额导入非 JSON 文件报UnsupportedFileFormatError时按提示在存储设置中改用Tasks导入方式即可代理模式下媒体加载缓慢可结合RESOLVER_PROXY_MAX_RANGE_SIZE与RESOLVER_PROXY_TIMEOUT调优分块大小与单请求超时。延伸阅读各云存储独立配置指南storage_s3.md、storage_gcp.md、storage_azure.md、storage_redis.md、storage_local.md、storage_databricks.md存储安全访问见 security.md 中 Secure access to cloud storages 与 Source storage behind your VPC导出 JSON 格式见 export.md 中 Label Studio JSON format of annotated tasks核心实现源码label_studio/io_storages/base_models.py定义通用存储抽象、proxy_api.py实现预签名重定向与代理流式传输、s3/、gcs/、azure_blob/、redis/、localfiles/各自实现存储后端、配置项见 label_studio/core/settings/base.py【免费下载链接】label-studioLabel Studio is a multi-type data labeling and annotation tool with standardized output format项目地址: https://gitcode.com/GitHub_Trending/la/label-studio创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考