Label Studio 本地文件存储(Local Files Storage)完整指南:离线环境下的数据导入、文件服务与标注导出
2026/9/13 14:29:05 网站建设 项目流程

Label Studio 本地文件存储(Local Files Storage)完整指南:离线环境下的数据导入、文件服务与标注导出

【免费下载链接】label-studioLabel Studio is a multi-type data labeling and annotation tool with standardized output format项目地址: https://gitcode.com/GitHub_Trending/la/label-studio

导读

Label Studio 的 Local Files Storage(本地文件存储)让自托管部署可以在不依赖任何对象存储服务的前提下,直接从服务器文件系统读取图片、音频、视频、文档等媒体数据,并将标注结果写回磁盘。它专为离线(air-gapped)环境或"数据不允许离开宿主机"的工作流设计,在社区版中提供了开箱即用的"我的数据目录"自动探测机制。读完本文,你将掌握如何在 Label Studio 中配置本地文件存储的三个核心操作——导入/同步(Import/Sync)文件服务(Serve)标注导出(Export),理解其底层路径规范化、权限校验与缓存机制,并能够独立排查 403/404 等常见故障。

概述:三个核心操作

本地文件存储的核心价值体现在三个相互独立又彼此衔接的操作上:

  1. 导入/同步(Import/Sync)——扫描一个目录,为每个文件创建指向本地文件的标注任务;
  2. 文件服务(Serve)——通过/data/local-files/?d=...端点把文件字节流式传输给标注界面;
  3. 导出(Export)——把完成的标注以 JSON 文件形式写入目标目录。

架构:配置、导入、文件服务与导出的完整链路

整个本地文件存储的数据流可以用下面这张流程图概括(该图源自 localfiles/README.md 中的架构图,语义与代码实现一致):

Configuration: - 环境变量: LOCAL_FILES_SERVING_ENABLED, LOCAL_FILES_DOCUMENT_ROOT - 社区版自动探测: mydata / label-studio-data Import Flow: UI "Add Source Storage" → Serializer(normalize path, validate_connection) → LocalFilesImportStorage → Sync → iter_objects(扫描目录) → use_blob_urls=true: 为每个文件创建任务,URL 为 /data/local-files/?d=path → use_blob_urls=false: 读取 JSON 文件作为任务定义 → 任务写入数据库 → 建立 LocalFilesImportStorageLink File Serving Flow: 标注界面请求 /data/local-files/?d=relative/path → localfiles_data 视图 → 校验认证 → safe_join(DOCUMENT_ROOT, path) 规范化路径 → 查找所有 storage.path 是文件目录前缀的存储 → 校验 project.has_permission → 允许: 使用 RangedFileResponse 流式返回文件并带 ETag → 拒绝: 403 Forbidden;路径不存在: 404 Not Found Export Flow: 标注保存 → post_save 信号 → LocalFilesExportStorage.save_annotation → 写入 JSON 到 storage.path/annotation_id.json → 建立 LocalFilesExportStorageLink 标注删除 → pre_delete 信号 → delete_annotation 删除 JSON 文件

各环节对应的源码位置

环节源码文件关键实现
路径规范化functions.pynormalize_storage_path
目录自动探测functions.pyautodetect_local_files_root
存储模型与校验models.pyLocalFilesMixinvalidate_connection
文件服务端点views.pylocalfiles_data视图
REST APIapi.pyImport/Export 系列 API 视图
表单字段定义form_layout.yml前端表单布局
路由注册io_storages/urls.py/api/storages/localfiles//data/local-files/

关键概念

存储模型(Storage Models)

本地文件存储在数据库中对应四个模型类,职责划分非常清晰:

模型用途
LocalFilesMixin共享字段(pathregex_filteruse_blob_urls)与校验逻辑
LocalFilesImportStorage源存储(Source Storage):扫描目录、创建任务
LocalFilesExportStorage目标存储(Target Storage):把标注写成 JSON 文件
LocalFilesImportStorageLink把任务关联到导入存储(追踪"哪个文件创建了哪个任务")
LocalFilesExportStorageLink把标注关联到导出存储(追踪已导出的文件)

从源码看,LocalFilesMixin的三个核心字段定义在 models.py:

  • path:本地绝对路径(TextField),在clean()save()两个时机都会执行normalize_storage_path规范化;
  • regex_filter:过滤对象的正则表达式,命中才导入;
  • use_blob_urls:布尔值,决定文件是被当作 BLOB 生成 URL,还是被当作任务定义 JSON 解析(默认False)。

导入模式(Import Modes)

同步导入存储时,use_blob_urls决定文件如何变成任务:

  • use_blob_urls=True(默认"Files"模式):每个文件变成一个任务,任务中唯一的 data 字段指向/data/local-files/?d=<相对路径>。最适合标注图片、音频、视频这类单媒体文件。
  • use_blob_urls=False("Tasks"模式):每个.json/.jsonl文件被解析为任务定义,适用于任务结构复杂或有多个 data 字段的场景。

这一分支逻辑实现在LocalFilesImportStorageBase.get_data()(models.py)中:use_blob_urls=True时构造{settings.DATA_UNDEFINED_NAME: f'{settings.HOSTNAME}/data/local-files/?d={quote(relative_path)}'}形式的任务;否则调用load_tasks_json读取文件内容。表单中对应的选择项定义在 form_layout.yml,UI 文案为 "Files - Automatically creates a task for each storage object" 与 "Tasks - Treat each JSON or JSONL file as a task definition"。

目录扫描由iter_objects()(models.py)完成:它使用path.glob('*')path.rglob('*')(当recursive_scan开启时)遍历目录,按文件名升序排序(保证任务 ID 与文件名顺序一致),跳过目录项,并用regex_filter正则匹配文件名(regex.match,注意是 match 而非 search,即从文件名开头匹配)。

路径处理(Path Handling)

所有存储路径在保存前都会被规范化(normalize_storage_path,见 functions.py):

  • 去除尾部斜杠(/data/images//data/images);
  • 把反斜杠转换为当前操作系统的路径分隔符(Linux 上C:\dataC:/data);
  • 折叠冗余分隔符(/data//images/data/images)。

这一步是为了防止"存储路径与请求路径不一致"导致的 404 错误——因为权限检查(见下文)会对请求文件所在目录与storage.path做字符串前缀匹配,任何格式不一致都会导致匹配失败。

权限模型(Permission Model)

/data/local-files/?d=...端点强制执行四重校验(对应 views.py 的实现顺序):

  1. 用户必须已认证(视图装饰器@permission_classes([IsAuthenticated]));
  2. LOCAL_FILES_SERVING_ENABLED必须为true(否则直接返回 403);
  3. 请求文件的所在目录必须位于至少一个LocalFilesImportStorage.path之内——实现方式是LocalFilesImportStorage.objects.annotate(_full_path=Value(full_path_dir)).filter(_full_path__startswith=F('path')),即对请求文件目录做数据库前缀匹配;
  4. 用户必须对该存储所属项目有访问权限(storage.project.has_permission(request.user))。

配置指南

环境变量

变量默认值说明
LOCAL_FILES_SERVING_ENABLEDfalse必须设为true才能通过/data/local-files/提供文件服务
LOCAL_FILES_DOCUMENT_ROOT/(根目录)基础目录;所有存储路径必须是它的子目录
ENABLE_LOCAL_FILES_STORAGEtrue是否把 Local Files 作为存储选项展示

这些默认值与解析逻辑定义在 core/settings/base.py:

ENABLE_LOCAL_FILES_STORAGE = get_bool_env('ENABLE_LOCAL_FILES_STORAGE', default=True) LOCAL_FILES_SERVING_ENABLED = get_bool_env('LOCAL_FILES_SERVING_ENABLED', default=False) LOCAL_FILES_DOCUMENT_ROOT = get_env('LOCAL_FILES_DOCUMENT_ROOT', default=os.path.abspath(os.sep))

变量名可以加LABEL_STUDIO_HEARTEX_前缀(按此顺序检测),例如LABEL_STUDIO_LOCAL_FILES_SERVING_ENABLED=true

ENABLE_LOCAL_FILES_STORAGE=false时,Local Files 存储选项会在 API 注册阶段被隐藏——io_storages/all_api.py 中依据该开关决定是否注册相关视图。

社区版自动探测(Community Edition Auto-Detection)

LOCAL_FILES_DOCUMENT_ROOTLOCAL_FILES_SERVING_ENABLED都未设置时,社区版会自动在当前工作目录下查找mydatalabel-studio-data目录(候选名定义在 functions.py 的AUTO_ROOT_CANDIDATES元组中)。若找到,则把该目录设为文档根并开启本地文件服务。

对应逻辑在 core/settings/base.py:

if ( VERSION_EDITION == 'Community' and not has_env('LOCAL_FILES_DOCUMENT_ROOT') and not has_env('LOCAL_FILES_SERVING_ENABLED') ): from label_studio.io_storages.localfiles.functions import autodetect_local_files_root _autodetected_root = autodetect_local_files_root() if _autodetected_root: LOCAL_FILES_DOCUMENT_ROOT = _autodetected_root LOCAL_FILES_SERVING_ENABLED = True

Docker 快捷方式:把宿主机目录挂载到容器内的/label-studio/mydata,即可在不设置任何环境变量的情况下启用本地文件存储。

生产环境配置

export LABEL_STUDIO_LOCAL_FILES_SERVING_ENABLED=true export LABEL_STUDIO_LOCAL_FILES_DOCUMENT_ROOT=/data/labelstudio # 目录结构: # /data/labelstudio/ ← DOCUMENT_ROOT # /data/labelstudio/project1/ ← 项目 1 的存储路径 # /data/labelstudio/project2/ ← 项目 2 的存储路径

注意:每个存储路径都必须是LOCAL_FILES_DOCUMENT_ROOT子目录,不能等于文档根本身。这一限制在validate_connection()(models.py)中有三重校验:

  1. 路径必须存在(Path.exists()),否则报 "does not exist";
  2. 路径不能与LOCAL_FILES_DOCUMENT_ROOT相同("cannot be the same ... by security reasons");
  3. 路径必须是文档根的子目录(document_root not in path.parents时报错),并提示如{DOCUMENT_ROOT}/dataset1的示例。

此外,若LOCAL_FILES_SERVING_ENABLEDFalse,创建存储时会直接报校验错误,提示先设置环境变量并重启;社区版还会附上community_auto_hint()的提示(创建mydatalabel-studio-data目录可自动启用)。

使用指南

用本地文件创建任务

  1. 在项目Settings → Cloud Storage → Add Source Storage → Local Files中配置导入存储;
  2. Absolute local path(绝对本地路径)设置为LOCAL_FILES_DOCUMENT_ROOT的子目录;
  3. 选择导入方式:
    • Files:每个媒体文件自动创建一个任务;
    • Tasks:把 JSON/JSONL 文件作为任务定义读取;
  4. 点击Sync扫描目录并创建任务。

表单中还可选填File Filter Regex(如.*csv.*(jpe?g|png|tiff).*\w+-\d+.text),只导入文件名匹配正则的文件(对应LocalFilesMixin.regex_filter字段与iter_objects中的过滤逻辑);Recursive scan开启后可递归扫描子目录。

手动导入任务(引用本地文件)

通过 JSON 手动导入任务时,用以下格式引用本地文件:

{ "data": { "image": "/data/local-files/?d=project1/images/photo.jpg", "audio": "/data/local-files/?d=project1/audio/recording.wav" } }

?d=之后的路径是相对于LOCAL_FILES_DOCUMENT_ROOT的。服务端收到请求后,会先posixpath.normpath(path).lstrip('/')规范化相对路径,再通过 Django 的safe_join(local_serving_document_root, path)拼接出安全绝对路径(views.py),从而把路径逃逸(path traversal)风险限制在文档根之内。

导出标注

  1. 在项目Settings → Cloud Storage → Add Target Storage → Local Files中配置目标存储;
  2. 标注保存后会自动写成 JSON 文件;
  3. 文件命名规则为<annotation_id>.json,位于存储路径下。

导出由 Django 信号驱动(models.py):

  • post_save信号export_annotation_to_local_files):标注保存后,遍历项目下所有io_storages_localfilesexportstorages关联的导出存储,调用save_annotation()
  • save_annotation()(models.py)把序列化后的标注用json.dump(..., indent=2)写入{storage_path}/{annotation_id}.json,并创建LocalFilesExportStorageLink
  • pre_delete信号delete_annotation_from_local_files):标注删除时,若对应存储的can_delete_objectsTrue,则删除磁盘上的 JSON 文件(文件已缺失时仅记录 warning),并清理关联记录。

测试用例 test_localfiles_export.py 验证了这一行为:can_delete_objects=True时删除标注会同步删除导出文件与链接;can_delete_objects=False时导出文件保留。同步工作流测试见 fsm/tests/test_storage_sync_workflows.py。

API 参考

REST 端点

端点方法说明
/api/storages/localfiles/GET, POST列出/创建导入存储
/api/storages/localfiles/{id}/GET, PATCH, DELETE管理指定导入存储
/api/storages/localfiles/{id}/syncPOST触发同步
/api/storages/export/localfiles/GET, POST列出/创建导出存储
/data/local-files/?d={path}GET提供文件内容服务(非 REST 端点)

上述路由注册在 io_storages/urls.py,除此之外还有localfiles/validate(连接校验)、localfiles/form(表单布局)、localfiles/files(文件列表)以及对应的 export 系列端点。对应的 API 视图类定义在 api.py,序列化器在 serializers.py——其中validate()会先规范化path,再实例化存储模型调用validate_connection(),把 Django/DRF 校验错误统一转为字符串格式返回给前端。

文件服务细节

/data/local-files/视图(views.py)的行为:

  • 服务被禁用或用户无权限 → 返回403
  • 文件不存在或没有匹配的存储 → 返回404
  • 客户端If-None-Match与当前 ETag 匹配 → 返回304 Not Modified
  • 支持HTTP Range 请求,用于视频/音频的拖动播放(通过RangedFileResponse实现,views.py)。

缓存实现细节:build_localfile_response()(views.py)基于文件修改时间纳秒与文件大小生成弱 ETag(格式W/"{mtime_ns:x}-{size:x}"),使浏览器可以在文件未变化时复用缓存;MIME 类型通过mimetypes.guess_type探测,未知类型回退为application/octet-stream

文件参考

文件用途
models.pyDjango 模型、normalize_storage_path应用、连接校验、信号处理器
views.py/data/local-files/端点,含 ETag 与 Range 支持
serializers.pyDRF 序列化器、路径规范化、错误格式化
api.pyREST API 视图类
functions.pynormalize_storage_pathautodetect_local_files_root
form_layout.ymlUI 表单字段定义

故障排查

常见问题

症状原因解决方案
/data/local-files/返回 403文件服务被禁用设置LOCAL_FILES_SERVING_ENABLED=true并重启
/data/local-files/返回 404没有匹配的存储或文件不存在检查存储路径是否为文件路径的前缀;确认文件存在
创建存储时校验报错路径不在文档根之下确保路径以LOCAL_FILES_DOCUMENT_ROOT开头且为子目录
图片显示为裂图路径不匹配(如尾部斜杠)路径现在会自动规范化;重新同步存储即可

调试步骤

  1. 检查环境变量:

    echo $LABEL_STUDIO_LOCAL_FILES_SERVING_ENABLED echo $LABEL_STUDIO_LOCAL_FILES_DOCUMENT_ROOT
  2. 直接测试文件访问(注意需携带授权 Token 的-H请求头):

    curl -I -H "Authorization: Token <your_token>" "http://localhost:8080/data/local-files/?d=project1/test.jpg"
  3. 在 Django shell 中核查存储配置:

    from io_storages.localfiles.models import LocalFilesImportStorage for s in LocalFilesImportStorage.objects.all(): print(f"{s.project.title}: {s.path}")

安全注意事项

  • 默认禁用LOCAL_FILES_SERVING_ENABLED=false防止意外暴露文件系统;
  • 路径包含:所有请求都基于LOCAL_FILES_DOCUMENT_ROOT校验(safe_join+ 前缀匹配),路径逃逸被限制在文档根内;
  • 项目权限:用户只能访问其有权限的、且与该文件存在前缀关联的项目存储中的文件;
  • 无目录列表:仅提供明确的文件路径服务,不提供目录浏览。

警告:不要在公开的多租户部署中启用本地文件服务。该特性专为单租户的本地(on-premise)部署设计。若你确实需要多租户场景,应改用对象存储(S3/GCS/Azure Blob)等具备独立凭据隔离能力的存储后端。

【免费下载链接】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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询