Javascript NodeJS-使用核心NodeJS和原始节点解决方案通过进度条上传文件

Javascript NodeJS-使用核心NodeJS和原始节点解决方案通过进度条上传文件,javascript,node.js,http,file-upload,Javascript,Node.js,Http,File Upload,Ryan Dahl说他发明了NodeJS来解决文件上传进度条问题()。使用2009年引入Node时可用的技术,在Express和更高级的客户端javascript库自动告诉您更新进度之前,NodeJS究竟是如何解决这个问题的 现在尝试只使用核心节点,我明白了,通过请求流,我可以查看头文件,获得总文件大小,然后获得每个数据块通过时的大小,告诉我完成百分比。但是我不知道如何将这些进度更新流回到浏览器,因为浏览器似乎直到request.end()才更新 我想再次总结一下NodeJS最初是如何解决这个进

Ryan Dahl说他发明了NodeJS来解决文件上传进度条问题()。使用2009年引入Node时可用的技术,在Express和更高级的客户端javascript库自动告诉您更新进度之前,NodeJS究竟是如何解决这个问题的

现在尝试只使用核心节点,我明白了,通过请求流,我可以查看头文件,获得总文件大小,然后获得每个数据块通过时的大小,告诉我完成百分比。但是我不知道如何将这些进度更新流回到浏览器,因为浏览器似乎直到request.end()才更新

我想再次总结一下NodeJS最初是如何解决这个进度更新问题的。WebSocket还不存在,因此您不能只打开与客户端的WebSocket连接,然后将进度更新流式传输回浏览器。是否使用了另一种客户端javascript技术

这是我迄今为止的尝试。进度更新流式传输到服务器端控制台,但浏览器仅在响应流接收到response.end()后更新

filechooser.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++);
};
var http=require('http');
var fs=需要('fs');
var server=http.createServer(函数(请求、响应){
setHeader('Content-Type','text/html;charset=UTF-8');
书面答复(200);
if(request.method=='GET'){
fs.createReadStream('filechooser.html').pipe(响应);
}
else if(request.method==“POST”){
var outputFile=fs.createWriteStream(“上传的文件”);
var total=请求.头文件['content-length'];
var进程=0;
write(‘开始上传’);
console.log('\n开始上载\n');
请求.on('data',函数(块){
FakenNetworkLatency(函数(){
outputFile.write(块);
进度+=chunk.length;
var perc=parseInt((进度/总计)*100);
log('完成百分比:'+perc+'%\n');
写入(“完成百分比:”+perc+“%”);
});
});
request.on('end',function(){
FakenNetworkLatency(函数(){
outputFile.end();
response.end(“上传的文件!”);
console.log('FILE upload\n');
});
});
}
});
listen(8080,函数(){
log('服务器正在侦听8080');
});
无功延迟=100//每个区块延迟100毫秒
var计数=0;
var fakeNetworkLatency=函数(回调){
setTimeout(函数(){
回调();
},延迟*count++);
};

首先,您的代码确实有效;节点发送分块响应,但浏览器只是等待更多响应,然后才懒得显示它

更多信息请访问:

第一次调用response.write()时,它将发送缓冲的 标题信息和客户端的第一个正文。第二次 调用response.write(),节点假定您将进行流式处理 数据,并单独发送。也就是说,响应被缓冲起来 到身体的第一块

如果您将content-type设置为html,比如
response.setHeader('content-type','text/html;charset=UTF-8')
,它使chrome呈现内容,但这只在我使用一系列设置超时调用时起作用,其中包含response.write调用;当我尝试使用你的代码时,它仍然没有更新dom,所以我深入挖掘

问题在于,在浏览器认为合适时呈现内容实际上取决于浏览器,因此我设置了代码来发送ajax请求以检查状态:

首先,我更新了服务器,将其状态存储在一个全局变量中,并打开一个“checkstatus”端点来读取它:

var http = require('http');
var fs = require('fs');
var status = 0;

var server = http.createServer(function (request, response) {
    response.writeHead(200);
    if (request.method === 'GET') {
        if (request.url === '/checkstatus') {
            response.end(status.toString());
            return;
        }
        fs.createReadStream('filechooser.html').pipe(response);
    }
    else if (request.method === 'POST') {
        status = 0;
        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');
            status = perc;
        });

        request.pipe(outputFile);

        request.on('end', function () {
            response.end('\nArchived File\n\n');
        });
    }

});

server.listen(8080, function () {
    console.log('Server is listening on 8080');
});
然后,我更新了filechooser.html以检查ajax请求的状态:

<!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>

Percent Complete: <span id="status">0</span>%

</body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script>
    var $status = $('#status');
    /**
     * When the form is submitted, begin checking status periodically.
     * Note that this is NOT long-polling--that's when the server waits to respond until something changed. 
     * In a prod env, I recommend using a websockets library with a long-polling fall-back for older broswers--socket.io is a gentleman's choice)
     */
    $('form').on('submit', function() {
        var longPoll = setInterval(function () {
            $.get('/checkstatus').then(function (status) {
                $status.text(status);

                //when it's done, stop annoying the server
                if (parseInt(status) === 100) {
                    clearInterval(longPoll);
                }
            });
        }, 500);
    });
</script>
</html>

完成百分比:0%
变量$status=$(“#status”);
/**
*提交表单后,开始定期检查状态。
*请注意,这不是长时间轮询——此时服务器等待响应,直到发生更改。
*在prod env中,我建议使用websockets库,为老broswers提供长时间的轮询回退——socket.io是绅士的选择)
*/
$('form')。在('submit',function()上{
var longPoll=setInterval(函数(){
$.get('/checkstatus')。然后(函数(状态){
$status.text(状态);
//完成后,停止骚扰服务器
if(parseInt(status)==100){
clearInterval(longPoll);
}
});
}, 500);
});
注意,尽管我没有结束响应,服务器仍然能够处理传入的状态请求

为了回答你的问题,Dahl看到一个flickr应用程序上传了一个文件,并进行了长时间的民意调查以检查其状态。他之所以这么做是因为服务器能够在继续上传的同时处理这些ajax请求。这是多任务的。看到他在14分钟后谈论这件事,甚至说,“这就是它的工作原理……”。几分钟后,他提到了iframe技术,并将长轮询与简单ajax请求区分开来。他说他想编写一个针对这些行为类型进行优化的服务器

无论如何,这在当时是不常见的。大多数web服务器软件一次只能处理一个请求。如果他们访问数据库,调用Web服务,与
<!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>

Percent Complete: <span id="status">0</span>%

</body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script>
    var $status = $('#status');
    /**
     * When the form is submitted, begin checking status periodically.
     * Note that this is NOT long-polling--that's when the server waits to respond until something changed. 
     * In a prod env, I recommend using a websockets library with a long-polling fall-back for older broswers--socket.io is a gentleman's choice)
     */
    $('form').on('submit', function() {
        var longPoll = setInterval(function () {
            $.get('/checkstatus').then(function (status) {
                $status.text(status);

                //when it's done, stop annoying the server
                if (parseInt(status) === 100) {
                    clearInterval(longPoll);
                }
            });
        }, 500);
    });
</script>
</html>