You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

加密工具处理PNG等二进制文件失效,求正确读取方式

搞定PNG加密的正确姿势:别用文本方式读二进制!

嘿,我懂你遇到的坑——处理TXT文件时加密解密都顺得很,一碰PNG就生成打不开的损坏文件,对比原文件还发现内容不对。问题出在你用UTF-8编码读取二进制文件上,这完全搞错了文件类型的处理逻辑!

为什么UTF-8读取PNG会坏?

UTF-8是给文本用的编码,它会把字节序列尝试解析成人类可读的字符。但PNG这类二进制文件里,有大量字节组合根本不是有效的UTF-8编码——Node.js碰到这种情况时,会自动把无效字节替换成「�」(Unicode替换字符),这直接就把原始的二进制信息弄丢了!写回去的文件自然和原文件不一样,系统当然认不出来是PNG。

而文本文件的内容本身是符合UTF-8规则的,所以用UTF-8读没问题,但二进制文件必须用原始字节模式处理。

正确的处理方案

下面是调整后的代码,专门适配二进制文件:

1. 修改读取流,获取原始Buffer

把读取流的encoding: 'UTF-8'删掉,让Node.js返回原始的Buffer(每个chunk就是单个字节的Buffer,因为你设了highWaterMark:1):

const reader = fs.createReadStream('./your-image.png', { flag: 'r', highWaterMark: 1 });

2. 重写二进制转换函数

原来的toBin和toStr是给字符串设计的,现在要换成直接操作字节的版本:

// 把单个字节(0-255的整数)转成8位二进制字符串
function byteToBin(byte) {
    // padStart(8, '0')确保不足8位时前面补零,保证是标准的8位二进制
    return byte.toString(2).padStart(8, '0');
}

// 把8位二进制字符串转回单个字节
function binToByte(binStr) {
    return parseInt(binStr, 2);
}

3. 调整流处理逻辑

现在chunk是Buffer,直接取它的第一个元素就是字节的数值,然后用上面的函数处理:

reader.on('data', function (chunk) { 
    // 从Buffer里取出单个字节的数值
    const originalByte = chunk[0];
    // 转成8位二进制
    const originalBin = byteToBin(originalByte);
    
    // 用你原来的加密函数处理(比如swap或者reverse)
    const encryptedBin = swap(originalBin);
    // 转回字节数值
    const encryptedByte = binToByte(encryptedBin);
    
    // 用Buffer包裹字节,写入文件(必须用Buffer,不能直接写数字)
    writer.write(Buffer.from([encryptedByte]));
    
    // 控制台打印对比(可选,方便调试)
    console.log(`原字节(十六进制): 0x${originalByte.toString(16).padStart(2, '0')} → 原二进制: ${originalBin} → 加密后二进制: ${encryptedBin} → 加密后字节: 0x${encryptedByte.toString(16).padStart(2, '0')}`);
});

4. 写入流也保持二进制模式

写入流不需要指定编码,默认就是二进制模式:

const writer = fs.createWriteStream('./encrypted-image.png', { flag: 'w' });

完整可运行的示例代码

const fs = require('fs');

// 你的加密函数保持不变
function swap(bin) { 
    return bin.slice(4, 8) + bin.slice(0, 4); 
} 
function reverse(bin) { 
    return bin.split("").reverse().join(""); 
}

// 适配二进制的转换函数
function byteToBin(byte) {
    return byte.toString(2).padStart(8, '0');
}
function binToByte(binStr) {
    return parseInt(binStr, 2);
}

// 处理PNG文件
const inputPath = './test.png';
const outputPath = './encrypted-test.png';

const reader = fs.createReadStream(inputPath, { flag: 'r', highWaterMark: 1 });
const writer = fs.createWriteStream(outputPath, { flag: 'w' });

reader.on('data', (chunk) => {
    const originalByte = chunk[0];
    const originalBin = byteToBin(originalByte);
    
    // 这里可以替换成你想用的加密函数,比如reverse(originalBin)
    const encryptedBin = swap(originalBin);
    const encryptedByte = binToByte(encryptedBin);
    
    writer.write(Buffer.from([encryptedByte]));
});

reader.on('end', () => {
    writer.end();
    console.log(`加密完成!输出文件: ${outputPath}`);
});

// 处理错误
reader.on('error', (err) => console.error('读取文件失败:', err));
writer.on('error', (err) => console.error('写入文件失败:', err));

用这个代码处理PNG,就能保证每个字节的原始信息都被正确处理,加密后的文件解密后就能正常打开啦。

内容的提问来源于stack exchange,提问作者RisksY

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 19:42:40