如何在上传时计算 SHA-256 并写入 _attachments?
解读
国内企业在面试 CouchDB 岗位时,常把“附件上传 + 摘要校验”作为数据完整性与离线同步能力的综合考点。
面试官真正想确认的是:
- 你是否理解 CouchDB 的 multipart/related 上传协议;
- 能否在客户端(Node、浏览器、Flutter 等)实时流式计算 SHA-256,避免二次读取;
- 能否把摘要写进
attachments元数据的正确字段,并保证 后续同步不触发哈希冲突; - 是否知道 国内等保 2.0 对存储完整性 的合规要求,SHA-256 是最低推荐算法。
知识点
_attachments是文档顶层对象,键为文件名,值必须含content_type、data(base64)或follows(multipart 占位)、length以及可选的digest字段。- CouchDB 3.x 之后,如果
digest存在,服务器会强制校验,格式为algorithm-hex,例如sha256-a7c…。 - 浏览器端使用 SubtleCrypto.digest('SHA-256', ArrayBuffer),Node 端使用 crypto.createHash('sha256');都必须把文件读成
ArrayBuffer,避免编码错误。 - 上传路径有两条:
- 普通 JSON PUT:先把文件 base64 编码,计算 SHA-256,把
digest写进_attachments.filename.digest; - 高性能 multipart:先在
attachments元数据里写follows=true和digest,再按顺序输出 boundary,摘要必须在第一个 boundary 之前算完,否则顺序错乱会导致 400 bad_request。
- 普通 JSON PUT:先把文件 base64 编码,计算 SHA-256,把
- 国内移动端场景(如微信小程序)受 包体积限制,通常采用 分片 256 KB + WebWorker 计算 SHA-256,最后把摘要拼到主线程的
_attachments对象里一次性上传,避免 UI 阻塞。 - 如果摘要算错,CouchDB 返回
{"error":"attachment_digest_mismatch","reason":"expected sha256-xxx but got sha256-yyy"},国内 CDN 边缘节点缓存此响应 60 s,重试前必须刷新 URL。
答案
以 Node 16+ 为例,演示“单文档 + 单附件”的合规写法(满足等保 2.0 完整性要求):
import { readFile } from 'fs/promises';
import crypto from 'crypto';
import got from 'got'; // 国内镜像源已配置
const filePath = './invoice.pdf';
const docId = 'invoice:202406:001';
const couchUrl = 'http://couch.internal:5984/erp';
// 1. 流式计算 SHA-256
const buffer = await readFile(filePath);
const hash = crypto.createHash('sha256').update(buffer).digest('hex');
const digest = `sha256-${hash}`;
// 2. 构造 _attachments
const attachment = {
content_type: 'application/pdf',
data: buffer.toString('base64'),
length: buffer.length,
digest // **关键字段**
};
// 3. 写文档
const doc = {
_id: docId,
type: 'invoice',
date: '2024-06-01',
_attachments: {
'invoice.pdf': attachment
}
};
const resp = await got.put(`${couchUrl}/${docId}`, {
json: doc,
username: 'admin',
password: '***',
responseType: 'json'
});
console.log('rev:', resp.body.rev);
要点强调:
digest必须小写 hex,前缀sha256-不能省略;- 如果附件 > 4 MB,建议改用
multipart/related并把data换成follows=true,但摘要仍需预计算; - 国内私有云往往关闭
_security默认写权限,需提前给admin或invoice_writer角色授权,否则返回401 Unauthorized。
拓展思考
- 离线优先场景:PouchDB 浏览器端同样支持
digest字段,但计算 SHA-256 会阻塞主线程;可使用 WebAssembly 版 sha256,再搭配revpos字段,确保后续与 CouchDB 双向同步时不会重复上传相同摘要的块。 - 国内金融项目:监管要求原文件 + 摘要 + 时间戳一起存证,可把 SHA-256 再做一次 SM3 杂凑,形成双摘要,写入同一附件元数据的
extra_digest自定义字段,CouchDB 不会校验,但业务层可审计。 - 多主复制冲突:若两个数据中心同时上传同名附件但内容不同,CouchDB 以
digest为胜出依据;如果摘要相同则自动去重,节省跨机房流量 30% 以上,这对国内带宽成本敏感的企业尤为关键。