在Node.js中使用HTTP上传文件的方法

开发环境
我们将使用 Visual Studio Express 2013 for Web 作为开发环境, 不过它还不能被用来做 Node.js 开发。为此我们需要安装 Node.js Tools for Visual Studio。  装好后 Visual Studio Express 2013 for Web 就会转变成一个 Node.js IDE 环境,提供创建这个应用所需要的所有东西.。而基于这里提供的指导,我们需要:

  • 下载安装 Node.js  Windows 版,选择适用你系统平台的版本, Node.js (x86) 或者Node.js (x64)
  • 下载并安装 Node.js 的 Visual Studio 工具。

安装完成后我们就会运行 Visual Studio Express 2013 for Web, 并使用 Node.js 的交互窗口来验证安装. Node.js 的交互窗口可以再 View->Other Windows->Node.js Interactive Window 下找到. Node.js 交互窗口运行后我们要输入一些命令检查是否一切OK.

Figure 1 Node.js Interactive Window

现在我们已经对安装进行了验证,我们现在就可以准备开始创建支持GB级文件上传的Node.js后台程序了. 开始我们先创建一个新的项目,并选择一个空的 Node.js Web应用程序模板.

Figure 2 New project using the Blank Node.js Web Application template

项目创建好以后,我们应该会看到一个叫做 server.js 的文件,还有解决方案浏览器里面的Node包管理器 (npm).

图3 解决方案管理器里面的 Node.js 应用程序

server.js 文件里面有需要使用Node.js来创建一个基础的hello world应用程序的代码.

Figure 4 The Hello World application
 我现在继续把这段代码从 server.js 中删除,然后在Node.js中穿件G级别文件上传的后端代码。下面我需要用npm安装这个项目需要的一些依赖:

  • Express - Node.js网页应用框架,用于构建单页面、多页面以及混合网络应用
  • Formidable - 用于解析表单数据,特别是文件上传的Node.js模块
  • fs-extra - 文件系统交互模块

图5 使用npm安装所需模块

模块安装完成后,我们可以从解决方案资源管理器中看到它们。

图6 解决方案资源管理器显示已安装模块

下一步我们需要在解决方案资源管理器新建一个 "Scripts" 文件夹并且添加  "workeruploadchunk.js" 和   "workerprocessfile.js" 到该文件夹。我们还需要下载jQuery 2.x 和  SparkMD5库并添加到"Scripts"文件夹。 最后还需要添加 "Default.html" 页面。

 创建Node.js后台

首先我们需要用Node.js的"require()"函数来导入在后台上传G级文件的模块。注意我也导入了"path"以及"crypto" 模块。"path"模块提供了生成上传文件块的文件名的方法。"crypto" 模块提供了生成上传文件的MD5校验和的方法。

// The required modules
var express = require('express');
var formidable = require('formidable');
var fs = require('fs-extra');
var path = require('path');
var crypto = require('crypto');

下一行代码就是见证奇迹的时刻。

代码如下:

<span style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-removed: initial; background-repeat: initial; background-size: initial; color: #000066; font-family: Consolas; font-size: 9pt;">var</span><span style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-removed: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 9pt;"> app <span style="color: #339933;">=</span> express<span style="color: #009900;">()</span><span style="color: #339933;">;</span></span>

这行代码是用来创建express应用的。express应用是一个封装了Node.js底层功能的中间件。如果你还记得那个由Blank Node.js Web应用模板创建的"Hello World" 程序,你会发现我导入了"http"模块,然后调用了"http.CreateServer()"方法创建了 "Hello World" web应用。我们刚刚创建的express应用内建了所有的功能。

现在我们已经创建了一个express应用,我们让它呈现之前创建的"Default.html",然后让应用等待连接。

// Serve up the Default.html page
app.use(express.static(__dirname, { index: 'Default.html' }));  

// Startup the express.js application
app.listen(process.env.PORT || 1337);  

// Path to save the files
var uploadpath = 'C:/Uploads/CelerFT/';

express应用有app.VERB()方法,它提供了路由的功能。我们将使用app.post()方法来处理"UploadChunk" 请求。在app.post()方法里我们做的第一件事是检查我们是否在处理POST请求。接下去检查Content-Type是否是mutipart/form-data,然后检查上传的文件块大小不能大于51MB。

// Use the post method for express.js to respond to posts to the uploadchunk urls and
// save each file chunk as a separate file
app.post('*/api/CelerFTFileUpload/UploadChunk*', function(request,response) {  

 if (request.method === 'POST') {
  // Check Content-Type
  if (!(request.is('multipart/form-data'))){
   response.status(415).send('Unsupported media type');
   return;
  }  

  // Check that we have not exceeded the maximum chunk upload size
  var maxuploadsize =51 * 1024 * 1024;  

  if (request.headers['content-length']> maxuploadsize){
   response.status(413).send('Maximum upload chunk size exceeded');
   return;
  }

一旦我们成功通过了所有的检查,我们将把上传的文件块作为一个单独分开的文件并将它按顺序数字命名。下面最重要的代码是调用fs.ensureDirSync()方法,它使用来检查临时目录是否存在。如果目录不存在则创建一个。注意我们使用的是该方法的同步版本。

// Get the extension from the file name
var extension =path.extname(request.param('filename'));  

// Get the base file name
var baseFilename =path.basename(request.param('filename'), extension);  

// Create the temporary file name for the chunk
var tempfilename =baseFilename + '.'+
request.param('chunkNumber').toString().padLeft('0', 16) + extension + ".tmp";  

// Create the temporary directory to store the file chunk
// The temporary directory will be based on the file name
var tempdir =uploadpath + request.param('directoryname')+ '/' + baseFilename;  

// The path to save the file chunk
var localfilepath =tempdir + '/'+ tempfilename;  

if (fs.ensureDirSync(tempdir)) {
 console.log('Created directory ' +tempdir);
}

正如我之前提出的,我们可以通过两种方式上传文件到后端服务器。第一种方式是在web浏览器中使用FormData,然后把文件块作为二进制数据发送,另一种方式是把文件块转换成base64编码的字符串,然后创建一个手工的multipart/form-data encoded请求,然后发送到后端服务器。

所以我们需要检查一下是否在上传的是一个手工multipart/form-data encoded请求,通过检查"CelerFT-Encoded"头部信息,如果这个头部存在,我们创建一个buffer并使用request的ondata时间把数据拷贝到buffer中。

在request的onend事件中通过将buffer呈现为字符串并按CRLF分开,从而从 multipart/form-data encoded请求中提取base64字符串。base64编码的文件块可以在数组的第四个索引中找到。

通过创建一个新的buffer来将base64编码的数据重现转换为二进制。随后调用fs.outputFileSync()方法将buffer写入文件中。

// Check if we have uploaded a hand crafted multipart/form-data request
// If we have done so then the data is sent as a base64 string
// and we need to extract the base64 string and save it
if (request.headers['celerft-encoded']=== 'base64') {  

 var fileSlice = newBuffer(+request.headers['content-length']);
 var bufferOffset = 0;  

 // Get the data from the request
 request.on('data', function (chunk) {
  chunk.copy(fileSlice , bufferOffset);
  bufferOffset += chunk.length;
 }).on('end', function() {
  // Convert the data from base64 string to binary
  // base64 data in 4th index of the array
  var base64data = fileSlice.toString().split('\r\n');
  var fileData = newBuffer(base64data[4].toString(), 'base64');  

  fs.outputFileSync(localfilepath,fileData);
  console.log('Saved file to ' +localfilepath);  

  // Send back a sucessful response with the file name
  response.status(200).send(localfilepath);
  response.end();
 });
}

二进制文件块的上传是通过formidable模块来处理的。我们使用formidable.IncomingForm()方法得到multipart/form-data encoded请求。formidable模块将把上传的文件块保存为一个单独的文件并保存到临时目录。我们需要做的是在formidable的onend事件中将上传的文件块保存为里一个名字。

else {
 // The data is uploaded as binary data.
 // We will use formidable to extract the data and save it
 var form = new formidable.IncomingForm();
 form.keepExtensions = true;
 form.uploadDir = tempdir;  

 // Parse the form and save the file chunks to the
 // default location
 form.parse(request, function (err, fields, files) {
  if (err){
   response.status(500).send(err);
   return;
  }  

 //console.log({ fields: fields, files: files });
 });  

 // Use the filebegin event to save the file with the naming convention
 /*form.on('fileBegin', function (name, file) {
 file.path = localfilepath;
});*/  

form.on('error', function (err) {
  if (err){
   response.status(500).send(err);
   return;
  }
 });  

 // After the files have been saved to the temporary name
 // move them to the to teh correct file name
 form.on('end', function (fields,files) {
  // Temporary location of our uploaded file
  var temp_path = this.openedFiles[0].path;  

  fs.move(temp_path , localfilepath,function (err){  

   if (err) {
    response.status(500).send(err);
    return;
   }
   else {
    // Send back a sucessful response with the file name
    response.status(200).send(localfilepath);
    response.end();
   }
  });
 });  

// Send back a sucessful response with the file name
//response.status(200).send(localfilepath);
//response.end();
}
}

app.get()方法使用来处理"MergeAll"请求的。这个方法实现了之前描述过的功能。

// Request to merge all of the file chunks into one file
app.get('*/api/CelerFTFileUpload/MergeAll*', function(request,response) {  

 if (request.method === 'GET') {  

  // Get the extension from the file name
  var extension =path.extname(request.param('filename'));  

  // Get the base file name
  var baseFilename =path.basename(request.param('filename'), extension);  

  var localFilePath =uploadpath + request.param('directoryname')+ '/' + baseFilename;  

  // Check if all of the file chunks have be uploaded
  // Note we only wnat the files with a *.tmp extension
  var files =getfilesWithExtensionName(localFilePath, 'tmp')
  /*if (err) {
   response.status(500).send(err);
   return;
  }*/ 

  if (files.length !=request.param('numberOfChunks')){
   response.status(400).send('Number of file chunks less than total count');
   return;
  }  

  var filename =localFilePath + '/'+ baseFilename +extension;
  var outputFile =fs.createWriteStream(filename);  

  // Done writing the file
  // Move it to top level directory
  // and create MD5 hash
  outputFile.on('finish', function (){
   console.log('file has been written');
   // New name for the file
   var newfilename = uploadpath +request.param('directoryname')+ '/' + baseFilename
   + extension;  

   // Check if file exists at top level if it does delete it
   //if (fs.ensureFileSync(newfilename)) {
   fs.removeSync(newfilename);
   //} 

   // Move the file
   fs.move(filename, newfilename ,function (err) {
    if (err) {
     response.status(500).send(err);
     return;
    }
    else {
     // Delete the temporary directory
     fs.removeSync(localFilePath);
     varhash = crypto.createHash('md5'),
      hashstream = fs.createReadStream(newfilename);  

     hashstream.on('data', function (data) {
      hash.update(data)
     });  

     hashstream.on('end', function (){
      var md5results =hash.digest('hex');
      // Send back a sucessful response with the file name
      response.status(200).send('Sucessfully merged file ' + filename + ", "
      + md5results.toUpperCase());
      response.end();
     });
    }
   });
  });  

  // Loop through the file chunks and write them to the file
  // files[index] retunrs the name of the file.
  // we need to add put in the full path to the file
  for (var index infiles) {
   console.log(files[index]);
   var data = fs.readFileSync(localFilePath +'/' +files[index]);
   outputFile.write(data);
   fs.removeSync(localFilePath + '/' + files[index]);
  }
  outputFile.end();
 } 

}) ;

注意Node.js并没有提供String.padLeft()方法,这是通过扩展String实现的。

// String padding left code taken from
// http://www.lm-tech.it/Blog/post/2012/12/01/String-Padding-in-Javascript.aspx
String.prototype.padLeft = function (paddingChar, length) {
 var s = new String(this);
 if ((this.length< length)&& (paddingChar.toString().length > 0)) {
  for (var i = 0; i < (length - this.length) ; i++) {
   s = paddingChar.toString().charAt(0).concat(s);
  }
 }
 return s;
} ;

其中一件事是,发表上篇文章后我继续研究是为了通过域名碎片实现并行上传到CeleFT功能。域名碎片的原理是访问一个web站点时,让web浏览器建立更多的超过正常允许范围的并发连接。 域名碎片可以通过使用不同的域名(如web1.example.com,web2.example.com)或者不同的端口号(如8000, 8001)托管web站点的方式实现。

示例中,我们使用不同端口号托管web站点的方式。

我们使用 iisnode 把 Node.js集成到 IIS( Microsoft Internet Information Services)实现这一点。 下载兼容你操作系统的版本 iisnode (x86) 或者  iisnode (x64)。 下载 IIS URL重写包。

一旦安装完成(假定windows版Node.js已安装),到IIS管理器中创建6个新网站。将第一个网站命名为CelerFTJS并且将侦听端口配置为8000。

图片7在IIS管理器中创建一个新网站

然后创建其他的网站。我为每一个网站都创建了一个应用池,并且给应用池“LocalSystem”级别的权限。所有网站的本地路径是C:\inetpub\wwwroot\CelerFTNodeJS。

图片8 文件夹层级

我在Release模式下编译了Node.js应用,然后我拷贝了server.js文件、Script文件夹以及node_modules文件夹到那个目录下。
 要让包含 iisnode 的Node.js的应用工作,我们需要创建一个web.config文件,并在其中添加如下得内容。

<defaultDocument>
 <files>
  <add value="server.js" />
 </files>
 </defaultDocument> 

 <handlers>
 <!-- indicates that the server.js file is a node.js application to be handled by the
 iisnode module -->
 <add name="iisnode" path="*.js" verb="*" modules="iisnode" />
 </handlers> 

 <rewrite>
 <rules>
  <rule name="CelerFTJS">
  <match url="/*" />
  <action type="Rewrite" url="server.js" />
  </rule> 

  <!-- Don't interfere with requests for node-inspector debugging -->
  <rule name="NodeInspector" patternSyntax="ECMAScript" stopProcessing="true">
  <match url="^server.js\/debug[\/]?" />
  </rule>
 </rules>
 </rewrite>

web.config中各项的意思是让iisnode处理所有得*.js文件,由server.js 处理任何匹配"/*"的URL。

如果你正确的做完了所有的工作,你就可以通过http://localhost:8000浏览网站,并进入CelerFT "Default.html"页面。

下面的web.config项可以改善 iisnode中Node.js的性能。

代码如下:

<span style="font-family: Consolas; font-size: 9pt;"><iisnode</span><span style="color: #009900; font-family: Consolas; font-size: 9.0pt;"> </span><span style="color: #000066; font-family: Consolas; font-size: 9.0pt;">node_env</span><span style="color: #009900; font-family: Consolas; font-size: 9.0pt;">=</span><span style="color: red; font-family: Consolas; font-size: 9.0pt;">"production"</span><span style="color: #009900; font-family: Consolas; font-size: 9.0pt;"> </span><span style="color: #000066; font-family: Consolas; font-size: 9.0pt;">debuggingEnabled</span><span style="color: #009900; font-family: Consolas; font-size: 9.0pt;">=</span><span style="color: red; font-family: Consolas; font-size: 9.0pt;">"false"</span><span style="color: #009900; font-family: Consolas; font-size: 9.0pt;"> </span><span style="color: #000066; font-family: Consolas; font-size: 9.0pt;">devErrorsEnabled</span><span style="color: #009900; font-family: Consolas; font-size: 9.0pt;">=</span><span style="color: red; font-family: Consolas; font-size: 9.0pt;">"false"</span><span style="color: #009900; font-family: Consolas; font-size: 9.0pt;"> </span><span style="color: #000066; font-family: Consolas; font-size: 9.0pt;">nodeProcessCountPerApplication</span><span style="color: #009900; font-family: Consolas; font-size: 9.0pt;">=</span><span style="color: red; font-family: Consolas; font-size: 9.0pt;">"0"</span><span style="color: #009900; font-family: Consolas; font-size: 9.0pt;"> </span><span style="color: #000066; font-family: Consolas; font-size: 9.0pt;">maxRequestBufferSize</span><span style="color: #009900; font-family: Consolas; font-size: 9.0pt;">=</span><span style="color: red; font-family: Consolas; font-size: 9.0pt;">"52428800"</span><span style="color: #009900; font-family: Consolas; font-size: 9.0pt;"> </span><span style="font-family: Consolas; font-size: 9pt;">/></span><span style="font-family: Consolas; font-size: 9.0pt;"><o:p></o:p></span>

 并行上传

为了使用域名碎片来实现并行上传,我不得不给Node.js应用做些修改。我第一个要修改的是让Node.js应用支持跨域资源共享。我不得不这样做是因为使用域碎片实际上是让一个请求分到不同的域并且同源策略会限制我的这个请求。

好消息是XMLttPRequest 标准2规范允许我这么做,如果网站已经把跨域资源共享打开,更好的是我不用为了实现这个而变更在"workeruploadchunk.js"里的上传方法。

// 使用跨域资源共享 // Taken from http://bannockburn.io/2013/09/cross-origin-resource-sharing-cors-with-a-node-js-express-js-and-sencha-touch-app/
var enableCORS = function(request,response, next){
 response.header('Access-Control-Allow-Origin', '*');
 response.header('Access-Control-Allow-Methods', 'GET,POST,OPTIONS');
 response.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, Content-
     Length, X-Requested-With' ) ; 

 // 拦截OPTIONS方法
 if ('OPTIONS' ==request.method){
  response.send(204);
 }
 else {
  next();
 }
} ;  

// 在表达式中使用跨域资源共享
app. use ( enableCORS ) ;

为了使server.js文件中得CORS可用,我创建了一个函数,该函数会创建必要的头以表明Node.js应用支持CORS。另一件事是我还需要表明CORS支持两种请求,他们是:

    简单请求:

1、只用GET,HEAD或POST。如果使用POST向服务器发送数据,那么发送给服务器的HTTP POST请求的Content-Type应是application/x-www-form-urlencoded, multipart/form-data, 或 text/plain其中的一个。

2、HTTP请求中不要设置自定义的头(例如X-Modified等)

    预检请求:

1、使用GET,HEAD或POST以外的方法。假设使用POST发送请求,那么Content-Type不能是application/x-www-form-urlencoded, multipart/form-data, or text/plain,例如假设POST请求向服务器发送了XML有效载荷使用了application/xml or text/xml,那么这个请求就是预检的。

2、在请求中设置自定义头(比如请求使用X-PINGOTHER头)。

在我们的例子中,我们用的是简单请求,所以我们不需要做其他得工作以使例子能够工作。

在  "workeruploadchunk.js" 文件中,我向  self.onmessage 事件添加了对进行并行文件数据块上传的支持.

// We are going to upload to a backend that supports parallel uploads.
// Parallel uploads is supported by publishng the web site on different ports
// The backen must implement CORS for this to work
else if(workerdata.chunk!= null&& workerdata.paralleluploads ==true){
 if (urlnumber >= 6) {
  urlnumber = 0;
 }  

 if (urlcount >= 6) {
  urlcount = 0;
 }  

 if (urlcount == 0) {
  uploadurl = workerdata.currentlocation +webapiUrl + urlnumber;
 }
 else {
  // Increment the port numbers, e.g 8000, 8001, 8002, 8003, 8004, 8005
  uploadurl = workerdata.currentlocation.slice(0, -1) + urlcount +webapiUrl +
  urlnumber;
 }  

 upload(workerdata.chunk,workerdata.filename,workerdata.chunkCount, uploadurl,
 workerdata.asyncstate);
 urlcount++;
 urlnumber++;
 }

在 Default.html 页面我对当前的URL进行了保存,因为我准备把这些信息发送给文件上传的工作程序. 只所以这样做是因为:

  • 我想要利用这个信息增加端口数量
  • 做了 CORS 请求,我需要把完整的 URL 发送给 XMLHttpRequest 对象.

代码如下:

<div class="MsoNoSpacing" style="background: #FFFF99;">
<span style="font-family: "Lucida Console"; font-size: 8.0pt;">// Save current protocol and host for parallel uploads</span></div>
<div class="MsoNoSpacing" style="background: #FFFF99;">
"font-family: 'Lucida Console'; font-size: 8pt;"><span style="color: #000066;">var</span><span style="font-family: 'Lucida Console'; font-size: 8pt;"> currentProtocol </span><span style="color: #339933; font-family: 'Lucida Console'; font-size: 8pt;">= </span><span style="font-family: 'Lucida Console'; font-size: 8pt;">window.</span><span style="color: #660066; font-family: 'Lucida Console'; font-size: 8pt;">location</span><span style="font-family: 'Lucida Console'; font-size: 8pt;">.</span><span style="color: #660066; font-family: 'Lucida Console'; font-size: 8pt;">protocol</span><span style="color: #339933; font-family: 'Lucida Console'; font-size: 8pt;">;</span></div>
<div class="MsoNoSpacing" style="background: #FFFF99;">
"font-family: 'Lucida Console'; font-size: 8pt;"><span style="color: #000066;">var</span><span style="font-family: 'Lucida Console'; font-size: 8pt;"> currentHostandPort </span><span style="color: #339933; font-family: 'Lucida Console'; font-size: 8pt;">=</span><span style="font-family: 'Lucida Console'; font-size: 8pt;"> window.</span><span style="color: #660066; font-family: 'Lucida Console'; font-size: 8pt;">location</span><span style="font-family: 'Lucida Console'; font-size: 8pt;">.</span><span style="color: #660066; font-family: 'Lucida Console'; font-size: 8pt;">host</span><span style="color: #339933; font-family: 'Lucida Console'; font-size: 8pt;">;</span></div>
<div class="MsoNoSpacing" style="background: #FFFF99;">
"font-family: 'Lucida Console'; font-size: 8pt;"><span style="color: #000066;">var</span><span style="font-family: 'Lucida Console'; font-size: 8pt;"> currentLocation </span><span style="color: #339933; font-family: 'Lucida Console'; font-size: 8pt;">= </span><span style="font-family: 'Lucida Console'; font-size: 8pt;">currentProtocol </span><span style="color: #339933; font-family: 'Lucida Console'; font-size: 8pt;">+</span><span style="font-family: 'Lucida Console'; font-size: 8pt;"> </span><span style="color: #3366cc; font-family: 'Lucida Console'; font-size: 8pt;">"//"</span><span style="font-family: 'Lucida Console'; font-size: 8pt;"> </span><span style="color: #339933; font-family: 'Lucida Console'; font-size: 8pt;">+</span><span style="font-family: 'Lucida Console'; font-size: 8pt;"> currentHostandPort</span><span style="color: #339933; font-family: 'Lucida Console'; font-size: 8pt;">;</span></div>

<span style="font-family: "Calibri","sans-serif"; font-size: 11.0pt; mso-ascii-theme-font: minor-latin; mso-bidi-font-family: "Times New Roman"; mso-bidi-theme-font: minor-bidi; mso-fareast-font-family: Calibri; mso-fareast-theme-font: minor-latin; mso-hansi-theme-font: minor-latin;">
</span>

<span style="font-family: "Calibri","sans-serif"; font-size: 11.0pt; mso-ascii-theme-font: minor-latin; mso-bidi-font-family: "Times New Roman"; mso-bidi-theme-font: minor-bidi; mso-fareast-font-family: Calibri; mso-fareast-theme-font: minor-latin; mso-hansi-theme-font: minor-latin;">The code below shows the modification made to the upload message.</span><span style="color: #006600; mso-bidi-font-style: italic;"><o:p></o:p></span>

<span style="font-family: "Calibri","sans-serif"; font-size: 11.0pt; mso-ascii-theme-font: minor-latin; mso-bidi-font-family: "Times New Roman"; mso-bidi-theme-font: minor-bidi; mso-fareast-font-family: Calibri; mso-fareast-theme-font: minor-latin; mso-hansi-theme-font: minor-latin;">
</span>

<span style="background-color: #ffff99; font-family: 'Lucida Console'; font-size: 8pt;">// Send and upload message to the webworker</span>

"background-color: #ffff99; font-family: 'Lucida Console'; font-size: 8pt;"><span style="color: #000066;">case</span><span style="background-color: #ffff99; font-family: 'Lucida Console'; font-size: 8pt;"> </span><span style="background-color: #ffff99; color: #3366cc; font-family: 'Lucida Console'; font-size: 8pt;">'upload'</span><span style="background-color: #ffff99; color: #339933; font-family: 'Lucida Console'; font-size: 8pt;">:</span>

<span style="background-color: #ffff99; font-family: 'Lucida Console'; font-size: 8pt;">// </span><span style="background-color: #ffff99; font-family: 'Lucida Console'; font-size: 8pt;">Check to see if backend supports parallel uploads</span>

var paralleluploads =false; 
if ($('#select_parallelupload').prop('checked')) { 
        paralleluploads = true; 

 
uploadworkers[data.id].postMessage({ 'chunk': data.blob, 'filename':data.filename, 
'directory': $("#select_directory").val(), 'chunkCount':data.chunkCount, 
'asyncstate':data.asyncstate,'paralleluploads':paralleluploads, 'currentlocation': 
currentLocation, 'id': data.id }); 
break;

最后修改了 CelerFT 接口来支持并行上传.

带有并行上传的CelerFT

这个项目的代码可以再我的 github 资源库上找到

(0)

相关推荐

  • Nodejs实现文件上传的示例代码

    笔者用nodejs做项目时需要用到文件上传的功能,在网上搜索了很多教程,找到了一个express的中间件,用于处理 multipart/form-data 类型的表单数据,可以很方便的将表单中的文件数据保存到服务器. 介绍 简单的用法 定义存储器 Multer作为express的一个中间件,我们可以很方便的自定义上传的文件目录以及保存的文件名.先看一个最简单的用法, demo1地址 : var express = require('express'); var multer = require(

  • node.js文件上传处理示例

    直入主题,在Node.js web 开发基本框架的前提下,我们来做一个文件上传功能 上传的handler比较简单,网上都能找到 var url=require('url'); var exec=require('child_process').exec; var querystring=require('querystring'); /********************************文件上传 第3方模块测试*************************/ function f

  • Node.js实现文件上传

    在工作中碰到了这样的需求,需要用nodejs 来上传文件,之前也只是知道怎么通过浏览器来上传文件, 用nodejs的话, 相当于模拟浏览器的行为. google 了一番之后, 明白了浏览器无非就是利用http协议来给服务器传输数据, 具体协议就是<RFC 1867 - Form-based File Upload in HTML>, 在浏览器上通过form 表单来上传文件就是通过这个协议,我们可以先看看浏览器给服务端发送了什么数据, 就可以依葫芦画瓢的把上传功能实现出来.说起form 表单上传

  • nodejs+express实现文件上传下载管理网站

    nodejs+express-实现文件上传下载管理的网站 项目Github地址:https://github.com/qcer/updo 后端:基于nodejs的express的web框架. 前端:bootstrap框架+vuejs.jquery等js库 功能点: dronzone.js实现文件拖拽上传.下载,可自定义传输容量. vuejs实现表格双向数据绑定. jquery.form.min.js表单插件,升级表单,实现表单提交回调. 纯css+jQuery实现一键返回顶部. 简单的ajax异

  • NodeJS使用formidable实现文件上传

    最近自学了一下NodeJS,然后做了一个小demo,实现歌曲的添加.修改.播放和删除的功能,其中自然要实现音乐和图片的上传功能.于是上网查找资料,找到了一个formidable插件,该插件可以很好的实现文件的上传功能.该小demo用到了MySQL数据库,所有的数据都存放到了数据库中.下面简单说一些如何使用. 1.创建app.js主文件 const express = require('express'); const router = require('./router'); const pat

  • node文件上传功能简易实现代码

    找了不少文件上传的相关模块,最后选择了比较常用,并且是express推荐使用的 multer 来实现文件上传,附上 GitHub地址 1. 开始 开始第一步,自然就是安装模块,不多说 npm install multer --save 这里简单说一下,因为文件上传是用 post 方法提交数据,所以上传的单文件或者多文件会作为一个 body 体添加到请求对象中,我们可以通过 req.file 或者 req.files 查看上传后文件的相关信息. 以单文件上传为例,req.file 返回一个对象:

  • Node.js模拟浏览器文件上传示例

    OSChina上发过了,那个也是我的,现在放到这来,哈哈 这段代码只能一次上传一个文件~~ 复制代码 代码如下: var path=require("path"); var fs=require("fs"); var http=require("http"); //post值payload var getfield=function(field, value) { return 'Content-Disposition: form-data; n

  • Node.js实现兼容IE789的文件上传进度条

    Nodejs对文件上传的处理 在Express4里req.files已经是undefined了:现在用的最多的可能就是formidable了,你知道了它有个progress事件,于是心中大喜,低版本IE的进度条有戏了:OK,试一下: form .on('error',function(err){ console.log(err); }) .on('aborted',function(){ console.log('aborted'); }) .on('progress',function(byt

  • 在Node.js中使用HTTP上传文件的方法

    开发环境 我们将使用 Visual Studio Express 2013 for Web 作为开发环境, 不过它还不能被用来做 Node.js 开发.为此我们需要安装 Node.js Tools for Visual Studio.  装好后 Visual Studio Express 2013 for Web 就会转变成一个 Node.js IDE 环境,提供创建这个应用所需要的所有东西..而基于这里提供的指导,我们需要: 下载安装 Node.js  Windows 版,选择适用你系统平台的

  • Node.js连接mongo数据库上传文件的方法步骤

    目录 效果 初始化 model 下创建index.js用于连接数据库时的信息 修改app.js文件里的信息 router下修改user.js文件 创建上传文件保存文件夹 在public下新建一个index.html 用于请求时的主页面 效果 初始化 创建项目文件夹mondo-express 安装包 npm i express-generator -g(作用一键生成项目列表) 在项目文件夹mondo-express用终端打开输入npx express-generator自动生成项目目录 安装包 终

  • JS中使用FormData上传文件、图片的方法

    关于FormData XMLHttpRequest Level 2添加了一个新的接口  ---- FormData 利用FormData对象,可以通过js用一些键值对来模拟一系列表单控件,可以使用XMLHttpRequest的 send( ) 方法来异步提交表单与普通的ajax相比,使用FormData的最大优点就是可以异步上传二进制文件 FormData对象 FormData对象,可以把所有表单元素的name与value组成一个queryString,提交到后台. 在使用ajax提交时,使用F

  • Node.js中使用mongoose操作mongodb数据库的方法

    如何利用mongoose将数据写入mongodb数据库? 1.利用npm下载安装mongoose; npm install mongoose 2.建立js文件,引入mongoose var mongoose = require('mongoose'); 3.mongoose.connect连接数据库 //连服务器 mongoose.connect('mongodb://127.0.0.1:27017/test'); //数据库的名字 var connection = mongoose.conne

  • 详解Node.js中path模块的resolve()和join()方法的区别

    关于Node.js中path模块的resolve()和join()方法的比较,对照着总结看例子差不多以后在写模块的时候思路就能很清晰了 resolve 作用:path.resolve() 该方法将一些的 路径/路径段 解析为绝对路径. 语法:path.resolve([...paths]) 说明: ...paths <string> 一个路径或路径片段的序列 如果没有传入 path 片段,或者path 片段长度为零(空字符),则 path.resolve() 会返回当前工作目录的绝对路径(相当

  • 总结Node.js中9种fs模块文件操作方法(文件夹递归删除知识)

    目录 一.前言 二.fs.mkdir()创建文件夹 2.1 最简单的案例 2.2 递归创建文件夹 三.fs.wirteFile()创建.写文件 3.1 创建并写入一个文件 3.2 重写文件并指定编码 3.3 写入GBK格式的文件 四.fs.appendFile()文件后追加内容 4.1 使用追加的方式创建并写入内容 4.2 追加内容 五.fs.stat()判断路径是目录还是文件 5.1 判断文件案例 六.fs.readFile()读取文件内容 6.1 以默认格式读取文件 6.2 以指定格式(这里

  • Vue中通过minio上传文件的详细步骤

    目录 1.minio的安装 2.minio.js文件 2.1连接minio 2.2通过stream流上传 2.3通过带预签名的url上传(最好是minio设置了链接永久访问) 2.4删除对象 补充:base64转blob 3.获取时间方法 4.minio设置链接永久访问(客户端) 5.解决跨域问题 6.存储大小限制问题 7.minio官方文档 8.minio设置链接永久访问(其他方法) 总结 在Vue中minio上传文件(stream流和链接) 1.minio的安装 直接使用npm或者cnpm下

  • JS使用base64格式上传文件

    本文实例为大家分享了JS使用base64格式上传文件的具体代码,供大家参考,具体内容如下 html页面 <input type="file" id="fielinput" /> <img id="txshow" style="width:100px;height:100px;"/> <br/>解析之后的base64数据:<br/> <p id="data"

  • JSP中图片的上传与显示方法实例详解

    本文实例讲述了JSP中图片的上传与显示方法.分享给大家供大家参考.具体如下: 1.引言 数据库应用程序,特别是基于WEB的数据库应用程序,常会涉及到图片信息的存储和显示.通常我们使用的方法是将所要显示的图片存在特定的目录下,在数据库中保存相应的图片的名称,在JSP中建立相应的数据源,利用数据库访问技术处理图片信息.但是,如果我们想动态的显示图片,上述方法就不能满足需要了.我们必须把图片存入数据库,然后通过编程动态地显示我们需要的图片.实际操作中,可以利用JSP的编程模式来实现图片的数据库存储和显

  • JS控制FileUpload的上传文件类型实例代码

    JS代码如下: function CheckFile(obj) { var array = new Array('gif', 'jpeg', 'png', 'jpg'); //可以上传的文件类型 if (obj.value == '') { alert("让选择要上传的图片!"); return false; } else { var fileContentType = obj.value.match(/^(.*)(\.)(.{1,8})$/)[3]; //这个文件类型正则很有用:)

随机推荐