Nodejs 为什么用http.request获取的网页源码,写入本地的文件的时候不是整个网页
Nodejs 为什么用http.request获取的网页源码,写入本地的文件的时候不是整个网页
var req = http.request({ host: ‘www.qq.com’, method: ‘get’ }, function(res) { res.setEncoding(‘utf8’); res.on(‘data’, function(data) { fs.writeFile(‘test.html’, data); }); }); req.end();
如题,上面的test.html文件打开后,不是www.qq.com那个网页的全部源码,只有部分,请问如何解决?
Node.js 为什么用 http.request
获取的网页源码,写入本地的文件的时候不是整个网页
在使用 Node.js 的 http.request
方法获取网页源码时,经常会遇到一个问题:将获取到的数据写入本地文件时,文件中只包含了部分网页内容。这是因为数据可能被分多次传递,每次触发 data
事件时都会调用 fs.writeFile
方法来写入数据,导致之前的写入内容被覆盖或丢失。
解决方法
为了解决这个问题,我们需要累积所有的数据片段,并在所有数据都接收到之后再写入文件。可以通过以下步骤实现:
- 累积数据:在每次
data
事件触发时,将接收到的数据片段追加到一个变量中。 - 写入文件:在
end
事件触发时,将累积的所有数据一次性写入文件。
下面是修改后的示例代码:
const http = require('http');
const fs = require('fs');
const req = http.request({
host: 'www.qq.com',
method: 'GET'
}, function (res) {
let data = ''; // 用于累积数据
res.setEncoding('utf8');
res.on('data', function (chunk) {
data += chunk; // 追加数据片段
});
res.on('end', function () {
fs.writeFile('test.html', data, function (err) {
if (err) throw err;
console.log('Data written to file successfully.');
});
});
});
req.end();
解释
- 累积数据:我们创建了一个空字符串
data
来存储从服务器接收到的所有数据片段。每次data
事件触发时,我们将接收到的数据片段chunk
追加到data
变量中。 - 写入文件:当所有数据都接收完毕(即
end
事件触发)时,我们使用fs.writeFile
将累积的所有数据一次性写入文件test.html
。
这种方法确保了所有数据片段都被正确地累积起来,并且在所有数据接收完毕后再进行写入,从而避免了数据丢失或覆盖的问题。
页面太长,所以会分成几个部分,分别触发data事件。你这样写的结果就是只保存了最后的一个部分。应当先在内存中缓冲,最后一次性写入文件
var http = require('http'),
fs = require('fs');
http.get('http://www.qq.com', function( res ){
res.on('error', function(e){
console.log(e);
}).pipe(fs.createWriteStream('qq.html'));
});
接收完数据后会触发end事件: 正确写法如下: var req = http.request({ host: ‘www.qq.com’, method: ‘get’ }, function(res) { res.setEncoding(‘utf8’); var html = []; res.on(‘data’, function(data) { html.push(data); }); res.on(‘end’,function(){ fs.writeFile(‘test.html’, html.join("")); }); }); req.end();
要说答案3楼的是最佳的,我在这里再说具体点 问题的关键是楼主用了fs.writeFile方法,请看它的源码: 来自lib/fs.js
fs.writeFile = function(path, data, encoding_, callback) {
var encoding = (typeof(encoding_) == 'string' ? encoding_ : 'utf8');
var callback_ = arguments[arguments.length - 1];
callback = (typeof(callback_) == 'function' ? callback_ : null);
fs.open(path, 'w', 438 /*=0666*/, function(openErr, fd) {
if (openErr) {
if (callback) callback(openErr);
} else {
var buffer = Buffer.isBuffer(data) ? data : new Buffer('' + data,
encoding);
writeAll(fd, buffer, 0, buffer.length, 0, callback);
}
});
};
可以看到它使用fs.open方法,使用使用了w模式,而这个模式表示,如果文件不存在则创建或者清空它。于是在Buffer写完64K之后就清空了一遍test.html。要知道QQ的首页少说得有300来K。于是就成这结果了。
res.on(‘end’)
在这个问题中,http.request
获取的数据是分段传输的,而你在每次接收到数据时就立即写入文件。这会导致多次写入文件,每次只覆盖之前的部分内容,最终结果就是文件中只包含最后一次接收到的数据片段。
为了解决这个问题,你应该将所有接收到的数据片段先累积起来,然后再一次性写入文件。示例如下:
const http = require('http');
const fs = require('fs');
let dataBuffer = '';
const req = http.request({
host: 'www.qq.com',
method: 'GET'
}, function (res) {
res.setEncoding('utf8');
res.on('data', function (chunk) {
dataBuffer += chunk; // 累积数据片段
});
res.on('end', function () {
fs.writeFile('test.html', dataBuffer, function (err) {
if (err) throw err;
console.log('File is created successfully.');
});
});
});
req.end();
这段代码中,我们将每次接收到的数据片段累积到 dataBuffer
变量中。当所有数据接收完毕(即触发 end
事件)时,我们再将完整的内容写入文件。这样可以确保写入文件的是完整的网页源码。