如何将 Firestore 数据导出到 Excel
Excel 无法直接读取 Firestore,所以每条路线都要经过一个文件:在 Excel 中打开的 CSV,或由脚本替你写出的 .xlsx。 拿到文件是容易的部分。打开它时不让 Excel 弄乱文字、丢掉前导零或把所有内容塞进一列,才是大多数人卡住的地方, 本指南也会讲到这些。
更新于 2026年9月24日。 方法 1 中的扩展程序 Firexport 是我们开发的。方法 2 不需要它。
简短回答
1. 从 Firebase 控制台导出 CSV,然后在 Excel 中打开
Firebase 控制台的 Firestore 页面有一个查询构建器(Query builder),可以运行查询并把结果显示在 表格中,但没有任何下载它们的办法。Firexport 是一款免费的 Chrome 扩展程序,会在这些结果旁边添加一个“导出”按钮。
- 从 Chrome 应用商店安装 Firexport。
- 在 Firebase 控制台中打开 Firestore Database,从面板视图(Panel view)切换到查询构建器(Query builder)(“数据”标签页的右上角)。
- 在查询范围(Query scope)和路径(Path)下选择集合,需要的话添加过滤条件或限制, 然后点击运行(Run)。
- 点击结果上方的导出。Firexport 会遍历结果的每一页,下载一个 CSV 文件。
- 在 Excel 中,转到数据 > 从文本/CSV(Data > From Text/CSV)(“获取和转换数据”组),选择文件, 检查预览,然后点击加载(Load)。如果预览看起来不对,下面的表格会告诉你该改哪个设置。

Firexport 的 CSV 以 UTF-8 字节顺序标记开头,所以即使你只是双击文件,Excel 也能正确读取非英文文字。不过双击仍会让 Excel 猜测分隔符和类型,这就是为什么导入对话框是更稳妥的路线。在免费版中,时间戳按控制台显示的样子输出(例如 “August 24, 2026 at 1:16:09 PM UTC+9”),Excel 会把它留作文本。Firexport Advanced(一次性许可证)会写出 Excel 能转换成日期的 ISO 8601 时间戳,并还原被控制台用“…”截断的长映射和数组。
直接从控制台把你的下一个 Firestore 查询导出为能在 Excel 中干净打开的 CSV。
当 Excel 把 CSV 弄坏时
无论你用哪种方式导出,任何 CSV 都可能出现这些问题。所有修复都在从文本/CSV(From Text/CSV)预览的设置里。
| 你看到的现象 | 原因 | 修复 |
|---|---|---|
| 带重音的文字或非拉丁文字乱码:“José Müller”显示为“José Müller” | CSV 是不带字节顺序标记(BOM)的 UTF-8,所以 Excel 用系统的旧版编码来读取 | 使用以 BOM 开头的 CSV(Firexport 的就是),或把文件原始格式(File Origin)设为 65001: Unicode (UTF-8) |
| 每一行都挤在 A 列里 | 你所在地区的列表分隔符是分号(欧洲常见),而文件用的是逗号 | 把分隔符(Delimiter)设为逗号 |
| “00123”变成 123,20 位的 ID 变成 1.23457E+19 | Excel 会把任何看起来像数字的内容转成数字,而数字只保留 15 位 | 把数据类型检测设为不检测数据类型(Do not detect data types),让每一列都以文本导入 |
| 时间戳按文本而不是按日期排序 | 直接打开时,Excel 会把 2026-08-24T04:16:09Z 这样的 ISO 8601 文本留作文本。关闭类型检测后,每一列都是文本 | 在转换数据(Transform Data)中把该列的类型改为日期/时间/时区 |
映射和数组会以文本形式放在一个单元格里,因为一个单元格只能放一个值。如果你需要把它们的字段拆成列,在转换数据(Transform Data)中选中该列,使用解析 > JSON(Parse > JSON),然后展开。 这要求每个单元格都是有效的 JSON,Firexport Advanced 和下面的脚本总会这样写;免费版在无法把值重新解析为 JSON 时会写入控制台的文本。
2. 用 Admin SDK 脚本写出 .xlsx 文件
脚本可以跳过 CSV 这一步,直接写出 Excel 工作簿,每个单元格一开始就是正确的类型:数字是数字,时间戳是日期,ID 是文本,所以不会丢掉前导零。它需要 Node.js 和一个能读取 Firestore 的账号:项目 Owner 或 Editor,或者 Cloud Datastore Viewer 角色。
- 安装 Admin SDK 和 ExcelJS:
npm install firebase-admin exceljs - 登录以获取本地凭据:
gcloud auth application-default login。在服务器上,改为让GOOGLE_APPLICATION_CREDENTIALS指向服务账号密钥。 - 把下面的脚本保存为
export-to-excel.mjs,替换your-project-id,然后运行node export-to-excel.mjs users。
// Usage: node export-to-excel.mjs users → writes users.xlsx
import ExcelJS from 'exceljs';
import { initializeApp } from 'firebase-admin/app';
import {
getFirestore,
FieldPath,
Timestamp,
GeoPoint,
DocumentReference,
} from 'firebase-admin/firestore';
initializeApp({ projectId: 'your-project-id' });
const db = getFirestore();
const collection = process.argv[2];
if (!collection) throw new Error('Usage: node export-to-excel.mjs <collection>');
// Read the collection 1,000 documents at a time, so large collections don't time out.
const docs = [];
let last;
while (true) {
let query = db.collection(collection).orderBy(FieldPath.documentId()).limit(1000);
if (last) query = query.startAfter(last);
const page = await query.get();
if (page.empty) break;
docs.push(...page.docs);
last = page.docs[page.docs.length - 1];
}
// Firestore types become plain values: timestamps as ISO 8601, references as paths.
function plain(value) {
if (value instanceof Timestamp) return value.toDate().toISOString();
if (value instanceof DocumentReference) return value.path;
if (value instanceof GeoPoint) return { latitude: value.latitude, longitude: value.longitude };
if (value instanceof Uint8Array) return Buffer.from(value).toString('base64');
if (Array.isArray(value)) return value.map(plain);
if (value && typeof value === 'object') {
return Object.fromEntries(Object.entries(value).map(([key, v]) => [key, plain(v)]));
}
return value;
}
// A timestamp field becomes a real Excel date (in UTC). A cell holds one value, so maps and arrays
// go in as JSON text. Strings stay strings, so IDs like "00123" keep their leading zeros.
function cell(value) {
if (value instanceof Timestamp) return value.toDate();
const v = plain(value);
return v !== null && typeof v === 'object' ? JSON.stringify(v) : v;
}
const rows = docs.map((doc) => ({ documentId: doc.id, ...doc.data() }));
// Documents in one collection can have different fields, so the columns are all of them.
const columns = [...new Set(rows.flatMap((row) => Object.keys(row)))];
const workbook = new ExcelJS.Workbook();
const sheet = workbook.addWorksheet(collection.slice(0, 31), { views: [{ state: 'frozen', ySplit: 1 }] });
sheet.columns = columns.map((key) => ({ header: key, key, width: 20 }));
sheet.getRow(1).font = { bold: true };
for (const row of rows) {
const added = sheet.addRow(Object.fromEntries(columns.map((key) => [key, cell(row[key])])));
added.eachCell((c) => {
if (c.value instanceof Date) c.numFmt = 'yyyy-mm-dd hh:mm:ss';
});
}
await workbook.xlsx.writeFile(`${collection}.xlsx`);
console.log(`Wrote ${rows.length} documents to ${collection}.xlsx`);- 工作簿有一个以集合命名的工作表,带有加粗且冻结的标题行:一个
documentId列,然后是每个字段一列。 - 时间戳是显示为
yyyy-mm-dd hh:mm:ss的 Excel 日期,采用 UTC。映射、数组和地理位置点是 JSON 文本, 引用是文档路径。 - 一个 Excel 工作表可容纳 1,048,576 行,脚本在写入文件之前会把每一行都放在内存中。对于更大的集合,请使用CSV 指南中的 gcloud 和 BigQuery 路线。
Google 表格
Google 表格可以打开这两种文件。转到文件 > 导入(File > Import),上传 CSV 或 .xlsx, 然后选择放到哪里(新建电子表格、新建工作表或当前工作表)。
- 对于 CSV,把分隔符设为逗号(或保留“自动检测”)。
- 如果你的数据里有带前导零的 ID、电话号码或代码,请取消勾选将文本转换为数字、日期和公式(Convert text to numbers, dates, and formulas)。否则表格会把 “00123”变成 123。
- 一个电子表格所有工作表加起来最多可容纳 1000 万个单元格。
常见问题
Excel 能直接连接 Firestore 吗?
没有内置连接器:Excel 的“获取数据”里没有 Firestore 数据源。你需要先导出一个文件,要么是 Excel 导入的 CSV,要么是脚本写出的 .xlsx,如本指南所示。
为什么 Excel 在我的 CSV 里显示“José”这样的乱码?
文件是 UTF-8 但没有字节顺序标记,所以 Excel 用系统的旧版编码来读取。请使用以 BOM 开头的 CSV(Firexport 的就是),或通过数据 > 从文本/CSV(Data > From Text/CSV)导入并把文件原始格式(File Origin)设为 65001: Unicode (UTF-8)。
Excel 或 Google 表格能容纳多少行?
一个 Excel 工作表可容纳 1,048,576 行,一个单元格最多 32,767 个字符。一个 Google 表格电子表格总共可容纳 1000 万个单元格。对于更大的集合,请用 gcloud 和 BigQuery 导出为 CSV,并在那里处理数据。
导出 Firestore 数据要花钱吗?
每种方法都会至少读取一次每个导出的文档,Firestore 会把这些计为文档读取。在每日免费配额内不花钱。
