Ryan
Dahl表示,他发明了NodeJS来解决文件上传进度栏问题(//youtu.be/SAc0vQCC6UQ))。使用2009年Node推出时可用的技术,因此在Express和可以自动告诉您进度更新的更高级的客户端javascript库之前,NodeJS如何解决此问题?
现在尝试仅使用Core
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');
});
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++);
};
首先,您的代码确实有效。节点发送分块的响应,但是浏览器只是在等待更多响应,然后才显示它。
节点文档中的更多信息:
第一次调用response.write()时,它将把缓冲的头信息和第一个正文发送给客户端。第二次调用response.write()时,Node假设您将要流传输数据,并分别发送数据。也就是说,响应被缓冲到主体的第一块。
如果将content-type设置为html like response.setHeader('Content-Type', 'text/html; charset=UTF-8');
,它将使chrome呈现内容,但这只有在我使用了一系列set
timeout调用以及内部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>
请注意,尽管我没有结束响应,但是服务器仍然能够处理传入的状态请求。
因此,为回答您的问题,Dahl被一个flickr应用程序迷住了,他看到该文件上传了文件并经过长时间轮询以检查其状态。他之所以着迷,是因为服务器在继续处理上载时能够处理这些ajax请求。这是多任务的。看到他在这段视频中恰好是14分钟谈论它-
甚至说,“这就是它的工作原理……”。几分钟后,他提到了iframe技术,并将长轮询与简单的ajax请求区分开来。他指出,他想编写针对这些行为优化的服务器。
无论如何,在那些日子里这并不常见。大多数Web服务器软件一次只能处理一个请求。而且,如果他们去了一个数据库,调用了一个Web服务,又与文件系统进行了交互,或者类似的事情,那么进程将坐下来等待它完成,而不是在等待时处理其他请求。
如果要同时处理多个请求,则必须启动另一个线程或使用负载平衡器添加更多服务器。
另一方面,Nodejs通过执行非阻塞IO非常有效地利用了主流程。Node并不是第一个这样做的节点,但是在非阻塞IO领域中使它与众不同的是它的所有默认方法都是异步的,因此您必须调用“
sync”方法来做 错事 。这迫使用户去做 正确的 事。
另外,应该注意,选择javascript的原因是因为它已经是一种在事件循环中运行的语言。有人 提出
处理异步代码。您可以具有匿名函数和闭包,这使异步操作更易于维护。
我还想提到,使用Promise库还可以使编写异步代码更加简洁。例如,请查看bluebirdjs-它具有一个不错的“
promisify”方法,该方法将转换对象原型上具有回调签名(function(error(params){}))的函数以代替返回promise。
问题内容: 我正在使用AngularJS与Web服务进行交互,用于提取公开的各种实体。其中一些实体是图像,因此我需要能够使用“对象” 的动作在同一请求中发送二进制数据和文本字段。 如何在单个请求中使用AngularJS的服务发送数据并将图像上传到宁静的Web服务? 问题答案: 我进行了无数次搜索,尽管可能会错过它,但找不到该问题的解决方案:使用$ resource操作上传文件。 让我们举个例子:R
本文向大家介绍ASP.NET对大文件上传的解决方案,包括了ASP.NET对大文件上传的解决方案的使用技巧和注意事项,需要的朋友参考一下 首先,我们需要下载这个名为 RanUpLoad 的组件。 下载完成之后,两个 dll 文件添加到项目的引用中区,xml 文件也要复制在项目中的 bin 文件夹下,也就是最后三个文件都要存在于 bin 文件夹中。 接着,上传控件还是用 ASP.NET 中自带的 Fi
本文向大家介绍Spring Boot应用上传文件时报错的原因及解决方案,包括了Spring Boot应用上传文件时报错的原因及解决方案的使用技巧和注意事项,需要的朋友参考一下 问题描述 Spring Boot应用(使用默认的嵌入式Tomcat)在上传文件时,偶尔会出现上传失败的情况,后台报错日志信息如下:“The temporary upload location is not valid”。 原
问题内容: 我看到很多人在使用Selenium WebDriver的测试环境中上传文件时遇到问题。我使用了硒WebDriver和Java,并且遇到了同样的问题。我终于找到了解决方案,因此我将其发布在这里,希望对其他人有所帮助。 当我需要在测试中上传文件时,请单击按钮中的Webdriver,然后等待窗口“打开”弹出。然后,我将路径复制到剪贴板中的文件,然后将其粘贴到“打开”窗口中,然后单击“输入”。
本文向大家介绍asp.net批量多选文件上传解决方案,包括了asp.net批量多选文件上传解决方案的使用技巧和注意事项,需要的朋友参考一下 多选文件上传,已经非常多了,选择性多了可能有时候要比较下哪个更合适,结合到项目中使用更方便才是最重要的。很多的多选上传基本上都是调用的swf文件,确实用flash 或flex开发一个多选上传的功能很方便,比如flex里内置的FileReferenceList对
本文向大家介绍原生JS上传大文件显示进度条 php上传文件代码,包括了原生JS上传大文件显示进度条 php上传文件代码的使用技巧和注意事项,需要的朋友参考一下 JS原生上传大文件显示进度条,php上传文件,供大家参考,具体内容如下 在php.ini修改需要的大小: upload_max_filesize = 8M post_max_size = 10M memory_limit = 2