# 通过文件系统 API 与 Hub 交互

除了 [HfApi](/docs/huggingface_hub/v2.4.0/zh/package_reference/hf_api#huggingface_hub.HfApi) 之外，`huggingface_hub` 库还提供了 [HfFileSystem](/docs/huggingface_hub/v2.4.0/zh/package_reference/hf_file_system#huggingface_hub.HfFileSystem)，这是一个符合 Python 风格的、[兼容 fsspec](https://filesystem-spec.readthedocs.io/en/latest/) 的 Hugging Face Hub 文件接口。[HfFileSystem](/docs/huggingface_hub/v2.4.0/zh/package_reference/hf_file_system#huggingface_hub.HfFileSystem) 基于 [HfApi](/docs/huggingface_hub/v2.4.0/zh/package_reference/hf_api#huggingface_hub.HfApi) 构建，并提供了典型的文件系统风格操作，例如 `cp`、`mv`、`ls`、`du`、`glob`、`get_file` 和 `put_file`。

> [!WARNING]
> [HfFileSystem](/docs/huggingface_hub/v2.4.0/zh/package_reference/hf_file_system#huggingface_hub.HfFileSystem) 提供 fsspec 兼容性，这对于需要该兼容性的库非常有用（例如，直接使用 `pandas` 读取 Hugging Face 数据集和存储桶）。然而，由于这一兼容层，它会引入额外的开销。为了获得更好的性能和可靠性，建议尽可能使用 [HfApi](/docs/huggingface_hub/v2.4.0/zh/package_reference/hf_api#huggingface_hub.HfApi) 方法。

## 用法

```python
>>> from huggingface_hub import hffs

>>> # List all files in a dataset directory
>>> hffs.ls("datasets/my-username/my-dataset-repo/data", detail=False)
['datasets/my-username/my-dataset-repo/data/train.csv', 'datasets/my-username/my-dataset-repo/data/test.csv']

>>> # List all files in a bucket directory
>>> hffs.ls("buckets/my-username/my-bucket/experiment-data", detail=False)
['bucket/my-username/my-bucket/data/train-0000.parquet', 'bucket/my-username/my-bucket/data/train-0001.parquet', ...]

>>> # List all ".csv" files in a dataset repository
>>> hffs.glob("datasets/my-username/my-dataset-repo/**/*.csv")
['datasets/my-username/my-dataset-repo/data/train.csv', 'datasets/my-username/my-dataset-repo/data/test.csv']

>>> # Read a remote file
>>> with hffs.open("datasets/my-username/my-dataset-repo/data/train.csv", "r") as f:
...     train_data = f.readlines()

>>> # Read the content of a remote file as a string
>>> train_data = hffs.read_text("datasets/my-username/my-dataset-repo/data/train.csv", revision="dev")

>>> # Write a remote file
>>> with hffs.open("datasets/my-username/my-dataset-repo/data/validation.csv", "w") as f:
...     f.write("text,label")
...     f.write("Fantastic movie!,good")
```

可选的 `revision` 参数可用于从特定提交（例如分支、标签名称或提交哈希值）运行操作。请注意，`revision` 与 Buckets 不兼容。

与 Python 内置的 `open` 不同，`fsspec` 的 `open` 默认使用二进制模式 `"rb"`。这意味着在文本模式下，您必须显式地将 mode 设置为 `"r"` 以进行读取，或设置为 `"w"` 以进行写入。目前尚不支持向文件追加内容（模式 `"a"` 和 `"ab"`）。

## 列出大型仓库

递归列出文件的操作（例如 `find` 和 `glob`）需要获取仓库的全部内容。Hub 会分页返回这些条目，且只有在收到上一页后才能请求下一页：列出包含 80 万个文件的仓库需要约 800 次顺序请求，因此耗时约 30 分钟。

为避免这种情况，[HfFileSystem](/docs/huggingface_hub/v2.4.0/zh/package_reference/hf_file_system#huggingface_hub.HfFileSystem) 会并行地逐个目录列出大型仓库：

```python
>>> from huggingface_hub import hffs

>>> # Takes ~30s on a repository holding 800k files
>>> files = hffs.glob("datasets/allenai/objaverse/**/*.glb", maxdepth=3)
```

并发请求的数量由 [HfFileSystem](/docs/huggingface_hub/v2.4.0/zh/package_reference/hf_file_system#huggingface_hub.HfFileSystem) 的 `max_workers` 参数控制。其默认值为 16，可以设置为 1 以禁用并行列表。请注意，传入 `maxdepth` 也会显著加快列表速度，因为 Hub 此时仅返回实际请求的条目。

## 保持列表最新

目录仅列出一次，其内容会被缓存，因此其他人刚刚修改的目录不会被自动更新。[HfFileSystem.invalidate_cache()](/docs/huggingface_hub/v2.4.0/zh/package_reference/hf_file_system#huggingface_hub.HfFileSystem.invalidate_cache) 会清除缓存的目录列表，可以针对单个路径（及其父目录）进行清除，也可以在无参数调用时清除所有缓存：

```python
>>> hffs.ls("buckets/my-username/my-bucket/data", detail=False)  # listing is cached
>>> # ... another process uploads a file to the bucket ...
>>> hffs.invalidate_cache("buckets/my-username/my-bucket/data")
>>> hffs.ls("buckets/my-username/my-bucket/data", detail=False)  # listed again
['buckets/my-username/my-bucket/data/train.csv', 'buckets/my-username/my-bucket/data/new-file.csv']
```

对于 bucket，`huggingface_hub` 可以为您完成此操作：Hub 会实时流式传输 bucket 的文件变更，而 [HfFileSystem](/docs/huggingface_hub/v2.4.0/zh/package_reference/hf_file_system#huggingface_hub.HfFileSystem) 会在变更到达时立即丢弃受影响的列表。使用 `live_follow=True` 即可启用：

```python
>>> from huggingface_hub import HfFileSystem
>>> hffs = HfFileSystem(live_follow=True)
>>> hffs.ls("buckets/my-username/my-bucket/data", detail=False)  # starts following that bucket
>>> # ... another process uploads a file to the bucket ...
>>> hffs.ls("buckets/my-username/my-bucket/data", detail=False)  # refreshed as changes arrive
['buckets/my-username/my-bucket/data/train.csv', 'buckets/my-username/my-bucket/data/new-file.csv']
```

它也可以通过 `fsspec.filesystem("hf", live_follow=True)` 经由 fsspec 传递，或者通过接受该参数的库（pandas、arrow 等）的 `storage_options` 参数传递。

关于它，有几点需要了解：

- **仅限存储桶。**模型、数据集和空间仓库均为 git 仓库，Hub 不会流式传输其变更：请向 [HfFileSystem.ls()](/docs/huggingface_hub/v2.4.0/zh/package_reference/hf_file_system#huggingface_hub.HfFileSystem.ls) 传入 `refresh=True`（或使用 [HfApi.list_repo_tree()](/docs/huggingface_hub/v2.4.0/zh/package_reference/hf_api#huggingface_hub.HfApi.list_repo_tree)）以查看新的提交。
- **仅刷新列表及其携带的元数据。**读取文件时始终会从 Hub 请求其内容，因此实时跟踪仅影响 `ls`、`info`、`exists`、`glob` 等命令的报告结果。
- 只要列出了某个存储桶，跟随操作便会立即开始，并且每个被跟随的存储桶都会保持一个后台连接处于打开状态，该连接由所有 [HfFileSystem](/docs/huggingface_hub/v2.4.0/zh/package_reference/hf_file_system#huggingface_hub.HfFileSystem) 实例共享。因此，该选项采用选择性启用（opt-in）的方式。

## 集成

[HfFileSystem](/docs/huggingface_hub/v2.4.0/zh/package_reference/hf_file_system#huggingface_hub.HfFileSystem) 可与任何集成了 `fsspec` 的库配合使用，前提是 URL 遵循以下格式：

```
hf://[<repo_type_prefix>]<repo_id>[@<revision>]/<path/in/repo>
```

`repo_type_prefix` 对于数据集为 `datasets/`，对于 Spaces 为 `spaces/`，而模型的 URL 中无需前缀。

除了仓库之外，[HfFileSystem](/docs/huggingface_hub/v2.4.0/zh/package_reference/hf_file_system#huggingface_hub.HfFileSystem) 还支持 Hugging Face Buckets，这是一种类似 S3 的对象存储（有关更多详细信息，请参阅[此指南](./buckets)）：

```
hf://buckets/<bucket_id>/<path/in/bucket>
```

以下是一些有趣的集成，其中 [HfFileSystem](/docs/huggingface_hub/v2.4.0/zh/package_reference/hf_file_system#huggingface_hub.HfFileSystem) 简化了与 Hub 的交互：

* 从/向 Hub 仓库读取/写入 [Pandas](https://pandas.pydata.org/pandas-docs/stable/user_guide/io.html#reading-writing-remote-files) DataFrame：

  ```python
  >>> import pandas as pd

  >>> # Read a remote CSV file into a dataframe
  >>> df = pd.read_csv("hf://datasets/my-username/my-dataset-repo/train.csv")
  >>> df = pd.read_csv("hf://buckets/my-username/my-bucket/train.csv")

  >>> # Write a dataframe to a remote CSV file
  >>> df.to_csv("hf://datasets/my-username/my-dataset-repo/test.csv")
  >>> df.to_csv("hf://buckets/my-username/my-bucket/test.csv")
  ```

相同的流程也可用于 [Dask](https://docs.dask.org/en/stable/how-to/connect-to-remote-data.html) 和 [Polars](https://pola-rs.github.io/polars/py-polars/html/reference/io.html) DataFrame。

* Querying （远程）使用 [DuckDB](https://duckdb.org/docs/guides/python/filesystems) 的 Hub 文件：

  ```python
  >>> from huggingface_hub import HfFileSystem
  >>> import duckdb

  >>> fs = HfFileSystem()
  >>> duckdb.register_filesystem(fs)
  >>> # Query a remote file and get the result back as a dataframe
  >>> fs_query_file = "hf://datasets/my-username/my-dataset-repo/data_dir/data.parquet"
  >>> df = duckdb.query(f"SELECT * FROM '{fs_query_file}' LIMIT 10").df()
  ```

* 使用 Hub 作为 [Zarr](https://zarr.readthedocs.io/en/stable/tutorial.html#io-with-fsspec) 数组存储：

  ```python
  >>> import numpy as np
  >>> import zarr

  >>> embeddings = np.random.randn(50000, 1000).astype("float32")

  >>> # Write an array to a repo
  >>> root = zarr.open_group("hf://my-username/my-model-repo/array-store", mode="w")
  >>> foo = root.create_group("embeddings")
  >>> foobar = foo.zeros(name="experiment_0", shape=(50000, 1000), chunks=(10000, 1000), dtype="f4")
  >>> foobar[:] = embeddings

  >>> # Read an array from a repo
  >>> root = zarr.open_group("hf://my-username/my-model-repo/array-store", mode="r")
  >>> first_row = root["embeddings/experiment_0"][0]
  ```

## 身份验证

在许多情况下，你必须使用 Hugging Face 账户登录才能与 Hub 进行交互。请参阅文档中的 [Authentication](../quick-start#authentication) 部分，以了解有关 Hub 上身份验证方法的更多信息。

也可以通过将 `token` 作为参数传递给 [HfFileSystem](/docs/huggingface_hub/v2.4.0/zh/package_reference/hf_file_system#huggingface_hub.HfFileSystem) 来以编程方式登录：

```python
>>> from huggingface_hub import HfFileSystem
>>> hffs = HfFileSystem(token=token)
```

如果您以这种方式登录，请注意在分享源代码时不要意外泄露令牌！

