Node.js从字符串⽣成⽂件流的实现⽅法
⼀.背景
在⽂件相关的数据加⼯等场景下,经常⾯临⽣成的物理⽂件应该如何处理的问题,⽐如:
⽣成的⽂件放到哪⾥,路径存在不存在?
临时⽂件何时清理,如何解决命名冲突,防⽌覆盖?
并发场景下的读写顺序如何保证?
……
对于读写物理⽂件带来的这些问题,最好的解决办法就是不写⽂件。然⽽,⼀些场景下想要不写⽂件可不那么容易,⽐如⽂件上传
⼆.问题
⽂件上传⼀般通过表单提交来实现,例如:
var FormData = require('form-data');
var fs = require('fs');
var form = new FormData();
form.append('my_file', fs.createReadStream('/foo/bar.jpg'));
form.submit('/upload', function(err, res) {
console.log(res.statusCode);
});
(摘⾃)
不想写物理⽂件的话,可以这样做:
const FormData = require('form-data');
const filename = '';
const content = '变⾝';
const formData = new FormData();
// 1.先将字符串转换成Buffer
const fileContent = Buffer.from(content);
// 2.补上⽂件meta信息
formData.append('file', fileContent, {
filename,
contentType: 'text/plain',
knownLength: fileContent.byteLength
});
也就是说,⽂件流除了能够提供数据外,还具有⼀些 meta 信息,如⽂件名、⽂件路径等,⽽这些信息是普通 Stream 所不具备的。那么,有没有办法凭空创建⼀个“真正的”⽂件流?
三.思路
要想创建出“真正的”⽂件流,⾄少有正反 2 种思路:
给普通流添上⽂件相关的 meta 信息
先拿到⼀个真正的⽂件流,再改掉其数据和 meta 信息
显然,前者更灵活⼀些,并且实现上能够做到完全不依赖⽂件
⽂件流的⽣产过程
沿着凭空创造的思路,探究 fs.createReadStream API 的之后发现,⽣产⽂件流的关键过程如下:
function ReadStream(path, options) {
// 1.打开path指定的⽂件
if (typeof this.fd !== 'number')
this.open();
}
ReadStream.prototype.open = function() {
fs.open(this.path, this.flags, de, (er, fd) => {
// 2.拿到⽂件描述符并持有
this.fd = fd;
// 3.开始流式读取数据
// read来⾃⽗类Readable,主要调⽤内部⽅法_read
// ref: github/nodejs/node/blob/v10.16.3/lib/_stream_readable.js#L390
});
};
ReadStream.prototype._read = function(n) {
// 4.从⽂件中读取⼀个chunk
let b = null;
if (bytesRead > 0) {
this.bytesRead += bytesRead;
b = thisPool.slice(start, start + bytesRead);
}
// 5.(通过触发data事件)吐出⼀个chunk,如果还有数据,Tick再次ad,直⾄this.push(null)触发'end'事件
// ref: github/nodejs/node/blob/v10.16.3/lib/_stream_readable.js#L207
this.push(b);
});
};
P.S.其中第 5 步相对复杂, this.push(buffer) 既能触发下⼀个 chunk 的读取( ad() ),也能在数据读完之后(通过 this.push(null) )触发 'end' 事件,具体见
重新实现⽂件流
既然已经摸清了⽂件流的⽣产过程,下⼀步⾃然是替换掉所有⽂件操作,直⾄⽂件流的实现完全不依赖⽂件,例如:
// 从⽂件中读取⼀个chunk
/* ... */
});
// 换成
this._fakeReadFile(this.fd, pool, pool.used, toRead, this.pos, (bytesRead) => {
/* ... */
});
// 从输⼊字符串对应的Buffer中copy出⼀个chunk
ReadStream.prototype._fakeReadFile = function(_, buffer, offset, length, position, cb) {
position = position || this.input._position;
// fake read file async
setTimeout(() => {
let bytesRead = 0;
if (position < this.input.byteLength) {
bytesRead = py(buffer, offset, position, position + length - 1);
this.input._position += bytesRead;
}
cb(bytesRead);
}, 0);
}
即从中剔除⽂件操作,⽤基于字符串的操作去替代它们
四.解决⽅案
nodejs字符串转数组
如此这般,就有了,⽤来凭空创建⽂件流:
string2fileStream('string-content') === fs.createReadStream(/* path to a text file with content 'string-content' */)`
例如:
const string2fileStream = require('string-to-file-stream');
const input = 'Oh, my great data!';
const s = string2fileStream(input);
<('data', (chunk) => {
assert.String(), input);
});
⽣成的流同样能够具有⽂件 meta 信息:
const string2fileStream = require('string-to-file-stream');
const formData = new FormData();
formData.append('file', string2fileStream('my-string-data', { path: './' }));
form.submit('/upload', function(err, res) {
console.log(res.statusCode);
});
⾜够以假乱真
参考资料
以上就是本⽂的全部内容,希望对⼤家的学习有所帮助,也希望⼤家多多⽀持。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。