Ryan Dahl曾表示他发明NodeJS是为了解决文件上传进度条问题 (https://youtu.be/SAc0vQCC6UQ)。在Node诞生于2009年时,使用当时可用的技术,比如Express和更先进的客户端JavaScript库不能自动提供进度更新。那么NodeJS究竟是如何解决这个问题的呢?
现在我试图仅使用核心NodeJS来解决这个问题。我可以通过请求流查看头部信息、获取文件总大小,然后获取每个数据块的大小以确定百分比完成情况。但我不知道如何将这些进度更新流式传输回浏览器,因为浏览器似乎要等到request.end()之后才会更新。
再次想弄清楚NodeJS最初是如何解决这个进度更新问题的。WebSocket还不存在,所以不能只是打开一个WebSocket连接并将进度更新流式传输回浏览器。是否有其他客户端JavaScript技术被使用了呢?
以下是我的尝试。进度更新被流式传输到服务器控制台,但浏览器只有在响应流收到response.end()后才会更新。
var http = require('http');
var fs = require('fs');
var server = http.createServer(function(request, response){
response.writeHead(200);
if(request.method === 'GET'){
fs.createReadStream('filechooser.html').pipe(response);
}
else if(request.method === 'POST'){
var outputFile = fs.createWriteStream('output');
var total = request.headers['content-length'];
var progress = 0;
request.on('data', function(chunk){
progress += chunk.length;
var perc = parseInt((progress/total)*100);
console.log('percent complete: '+perc+'%\n');
response.write('percent complete: '+perc+'%\n');
});
request.pipe(outputFile);
request.on('end', function(){
response.end('\nArchived File\n\n');
});
}
});
server.listen(8080, function(){
console.log('Server is listening on 8080');
});
文件选择器.html:
<!DOCTYPE html>
<html>
<body>
<form id="uploadForm" enctype="multipart/form-data" action="/" method="post">
<input type="file" id="upload" name="upload" />
<input type="submit" value="Submit">
</form>
</body>
</html>
这是一个更新的尝试。 浏览器现在显示进度更新,但我很确定这不是Ryan Dahl最初为生产场景想出的实际解决方案。他是否使用了长轮询?那个解决方案会是什么样子呢?
var http = require('http');
var fs = require('fs');
var server = http.createServer(function(request, response){
response.setHeader('Content-Type', 'text/html; charset=UTF-8');
response.writeHead(200);
if(request.method === 'GET'){
fs.createReadStream('filechooser.html').pipe(response);
}
else if(request.method === 'POST'){
var outputFile = fs.createWriteStream('UPLOADED_FILE');
var total = request.headers['content-length'];
var progress = 0;
response.write('STARTING UPLOAD');
console.log('\nSTARTING UPLOAD\n');
request.on('data', function(chunk){
fakeNetworkLatency(function() {
outputFile.write(chunk);
progress += chunk.length;
var perc = parseInt((progress/total)*100);
console.log('percent complete: '+perc+'%\n');
response.write('<p>percent complete: '+perc+'%');
});
});
request.on('end', function(){
fakeNetworkLatency(function() {
outputFile.end();
response.end('<p>FILE UPLOADED!');
console.log('FILE UPLOADED\n');
});
});
}
});
server.listen(8080, function(){
console.log('Server is listening on 8080');
});
var delay = 100; //delay of 100 ms per chunk
var count =0;
var fakeNetworkLatency = function(callback){
setTimeout(function() {
callback();
}, delay*count++);
};