图片交接时可以用 SHA-256 清单检查文件字节是否与确认版本一致。相同文件名不保证相同内容,改名也不改变字节指纹。下面用 Node.js 内置 crypto生成哈希,再分别报告缺失、变化和多余文件;它不判断商品事实,也不证明文件来源可信。
图片交接时可以用 SHA-256 清单检查文件字节是否与确认版本一致。相同文件名不保证相同内容,改名也不改变字节指纹。下面用 Node.js 内置 crypto生成哈希,再分别报告缺失、变化和多余文件;它不判断商品事实,也不证明文件来源可信。
团队从 Picset 或其他制作流程拿到结果后,[2] 可能再次压缩、改名或复制到不同目录。交接者记得一张最终图,接手者却只有同名文件。哈希提供可复查的内容对应,能够帮助发现传递过程变化,但谁确认过原始版本,仍需要可信业务记录。
内容哈希与文件身份分开保存
hashFiles 接收name与bytes,使用 createHash、update 与 digest计算十六进制SHA-256。[1] 文件名作为清单身份,字节作为哈希输入。名称为空或重复则拒绝执行;同内容不同名字可以得到相同哈希,但不因此自动合并,因为它们可能承担不同商品用途。

import { createHash } from 'node:crypto';
export function hashFiles(files) {
const seen=new Set();
return files.map(({name,bytes}) => {
if (typeof name!=='string' || !name || seen.has(name)) throw Error('file identity');
seen.add(name);
return { name, sha256:createHash('sha256').update(bytes).digest('hex') };
});
}
export function verifyManifest(expected,actual) {
const wanted=new Map(expected.map(x=>[x.name,x.sha256]));
const got=new Map(actual.map(x=>[x.name,x.sha256]));
if (wanted.size!==expected.length || got.size!==actual.length) throw Error('duplicate file name');
return {
missing:[...wanted.keys()].filter(x=>!got.has(x)),
changed:[...wanted.keys()].filter(x=>got.has(x)&&got.get(x)!==wanted.get(x)),
extra:[...got.keys()].filter(x=>!wanted.has(x))
};
}
保存为 file-manifest.mjs,在 .mjs 调用文件里用 readFile 获取 Buffer,再传给 hashFiles。expected 应来自确认交付的清单,actual 来自接手的文件,verifyManifest比较同名哈希。示例只返回结果,不修复缺文件,不删除extra,也不以哈希相同自动标记已经发布。
验证重复内容与交付变化
本次运行于 Node.js v24.16.0,对字符串hello计算出常见的SHA-256结果,断言与预期一致。随后构造同名内容变化、缺失项与多余项,验证分别进入changed、missing与extra。测试的PNG由本地构造,未使用真实客户交付数据,也没有测量大型文件性能。
如果图片重编码后视觉相似,字节哈希仍然会改变,这是正常结果。它检查确切内容,不检查感知相似度;不要因为两张图看着一样就手动覆盖changed。确认过的重新编码应建立新版本,保留为什么允许变化的记录。
expected清单也需要可信来源。攻击者同时替换图片和清单,普通哈希比较不能证明真实性;如果业务有来源认证要求,应另外设计签名、权限与传输流程。本文不是这套认证方案,也不将SHA-256称为版权证明、商品保真证明或安全保证。
实际目录读取建议限制文件大小并考虑流式哈希。示例已把所有bytes放在调用输入里,适合小样例说明,不适合不受控上传;生产环境也要校验路径范围和读取权限。异常要保留具体文件身份,不能让一个读取失败将整份清单悄悄缩短。
图文交付仍需要原片、资料与用途记录。哈希确认员工拿到的是同一份文件,不能确认那份文件写了正确参数;商品审核与格式预览要继续完成。将字节校验接到已有交付关系上,技术记录才服务于实际工作,不会变成替所有审核盖章的字段。