在Node.js中,如何在二进制文件中更改(覆盖)字节(在特定偏移处),而不会在其中添加字节或更改其长度?
在C语言中,我只需使用类似fopen()打开“r +”文件,使用fseek()到要更改的偏移量,然后使用fwrite()覆盖字节。在Node.js中相当于什么样子?
在C语言中,我只需使用类似fopen()打开“r +”文件,使用fseek()到要更改的偏移量,然后使用fwrite()覆盖字节。在Node.js中相当于什么样子?
fs.open(filename, "r+", (err, fd) => {
if(!err) {
fs.write(
fd, new Uint8Array([byte]), 0, 1, offset,
(err, bw, buf) => {
if(!err) {
// succesfully wrote byte to offset
}
}
);
}
});
最近我也需要做类似于你描述的事情。我需要在不改变大小的情况下更新可执行文件中的URL。关键是要在流上使用Transform。这个想法是,Transform将读取并写出您想要的确切数据,并仅修改您指定的字节。
这是一个Transform类,它可以在流中查找和替换。构造函数接受参数,用于指定应该被替换的块的起始和结束字节序列。还有一个padValue参数,用于保持相同的大小。
import { Transform } from 'stream'
export default class FindAndReplaceTransform extends Transform {
constructor(startBuffer, endBuffer, replacementValueBuffer, padValue, options) {
super(options);
this.startBuffer = startBuffer;
this.endBuffer = endBuffer;
this.replacementValueBuffer = replacementValueBuffer;
this.padValue = padValue;
}
_findInBuffer(sourceBuffer, searchBuffer) {
let searchFound = -1;
let lengthOfPartialMatch = 0;
for (let i = 0; i < sourceBuffer.length; i++) {
for (let j = 0; j < searchBuffer.length; j++) {
if (i + j >= sourceBuffer.length) {
if (j > 0) {
lengthOfPartialMatch = j;
}
break;
}
if (sourceBuffer[i + j] !== searchBuffer[j]) {
break;
}
if (j === searchBuffer.length - 1) {
searchFound = i;
}
}
if (searchFound >= 0 || lengthOfPartialMatch > 0) {
break;
}
}
return { searchFound, lengthOfPartialMatch };
}
_doReplacement(length) {
let replacementValueBuffer = this.replacementValueBuffer;
if (this.padValue !== undefined) {
replacementValueBuffer = Buffer.concat([replacementValueBuffer, Buffer.alloc(length - replacementValueBuffer.length, this.padValue)], length);
}
this.push(replacementValueBuffer);
}
//override
_transform(data, encoding, done) {
if(this.lengthOfPartialStartMatch){
data = Buffer.concat([this.startBuffer.slice(0, this.lengthOfPartialStartMatch), data], this.lengthOfPartialStartMatch + data.length);
delete this.lengthOfPartialStartMatch;
}
if(this.lengthOfPartialEndMatch){
data = Buffer.concat([this.endBuffer.slice(0, this.lengthOfPartialEndMatch), data], this.lengthOfPartialEndMatch + data.length);
this.replacementBuffer = this.replacementBuffer.slice(0, this.replacementBuffer.length - this.lengthOfPartialEndMatch);
delete this.lengthOfPartialEndMatch;
}
let startAlreadyFound = !!this.replacementBuffer
let { searchFound: startIndex, lengthOfPartialMatch: lengthOfPartialStartMatch } = this._findInBuffer(data, this.startBuffer);
let tail = data.slice(startIndex >= 0 && !startAlreadyFound ? startIndex : 0);
let { searchFound: endIndex, lengthOfPartialMatch: lengthOfPartialEndMatch } = this._findInBuffer(tail, this.endBuffer);
if (!startAlreadyFound && startIndex >= 0) {
this.push(data.slice(0, startIndex))
this.replacementBuffer = Buffer.alloc(0);
startAlreadyFound = true;
}
if (startAlreadyFound) {
if (endIndex >= 0) {
let replacementLength = this.replacementBuffer.length + endIndex + this.endBuffer.length;
this._doReplacement(replacementLength);
delete this.replacementBuffer;
if (endIndex + this.endBuffer.length < tail.length) {
let remainder = tail.slice(endIndex + this.endBuffer.length)
this._transform(remainder, encoding, done);
return;
}
} else {
this.lengthOfPartialEndMatch = lengthOfPartialEndMatch;
this.replacementBuffer = Buffer.concat([this.replacementBuffer, tail], this.replacementBuffer.length + tail.length);
}
} else {
this.lengthOfPartialStartMatch = lengthOfPartialStartMatch;
this.push(data.slice(0, data.length - lengthOfPartialStartMatch))
}
done();
}
//override
_flush(done) {
if (this.replacementBuffer) {
this.push(this.replacementBuffer)
}
if(this.lengthOfPartialStartMatch){
this.push(this.startBuffer.slice(0, this.lengthOfPartialStartMatch));
}
delete this.replacementBuffer;
delete this.lengthOfPartialStartMatch;
delete this.lengthOfPartialEndMatch;
done()
}
}
要使用上述转换,您可以像这样操作:
let stream = fs.createReadStream(inputFile);
let padding = 0x00;
let startSequence = Buffer.from('${', 'utf16le');
let endSequence = Buffer.from('}', 'utf16le');
let transform = new FindAndReplaceTransform(startSequence, endSequence, Buffer.from(replacementValue, 'utf16le'), paddingValue);
stream = stream.pipe(transform);
stream.pipe(fs.createWriteStream(outputFile));
显然,如果你只想在特定偏移处更改一个字节,Transform类会简单得多。我提供了上面的代码,因为我有它,如果你想做一些更复杂的事情,你可以参考它。
你需要确保实现的主要方法是_transform
方法。根据你的实现,你还可能需要实现_flush
方法。上面代码中的其他类方法是我替换代码的实现,对于Transform的工作不是必需的。