使用 HTTP 上傳 G 級的檔案之 Node.js 版本
不管喜歡與否,javascript無處不在。 我們可以在客戶端的前臺應用中找到它,也可以在大量的框架、類庫中找到它,而且可以在伺服器端的後臺應用中找到它。
近年來, Javascript越來越流行,這似乎是由於 Javascript 生態系統正在幫助提高生產率、減少入門所需的時間。 在我的 第一篇文章中,我介紹了使用 ASP.NET Web 後端 API 實現 G級檔案上傳,發表完這篇文章後,我決定試一下使用 Node.js 能否達到同樣的效果。 這意味著我需要實現 UploadChunk和 MergeAll方法,在 Node.js中我發表的 最後一篇文章談到了這一點。
開發環境
我們將使用 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" 頁面。這些都在我之前的 post 中介紹過。
建立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。
圖片9 URL重寫規則
如果你正確的做完了所有的工作,你就可以通過http://localhost:8000瀏覽網站,並進入CelerFT "Default.html"頁面。
web.config檔案被修改以支援如前面post中所解釋的大檔案的上傳,這裡我不會解釋所有的項。不過下面的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 資源庫上找到
原文地址:http://www.codeproject.com/Articles/835554/Gigabit-File-uploads-Over-HTTP-The-Node-js-version
相關文章
- Node.js使用HTTP上傳G級的大檔案Node.jsHTTP
- jmeter之http請求-如何上傳檔案JMeterHTTP
- HTTP檔案上傳原理HTTP
- 在 Node.js 裡使用 multiparty 上傳檔案Node.js
- HTTP multipart/form-data格式之檔案上傳HTTPORM
- netty系列之:搭建HTTP上傳檔案伺服器NettyHTTP伺服器
- Node.js:上傳檔案,服務端如何獲取檔案上傳進度Node.js服務端
- Ajax 之檔案上傳
- Netty接收HTTP檔案上傳及檔案下載NettyHTTP
- http不使用Form表單傳送檔案資料和非檔案資料(上傳篇)HTTPORM
- 使用fileinput上傳檔案
- Python模擬HTTP Post上傳檔案PythonHTTP
- Qt通過Http上傳檔案(路過)QTHTTP
- 使用jquery的FormData上傳檔案jQueryORM
- SpringMVC之檔案上傳SpringMVC
- 小程式之檔案上傳
- Web安全之檔案上傳Web
- Express檔案上傳之MulterExpress
- MVC檔案上傳 - 使用Request.Files上傳多個檔案MVC
- php檔案上傳之多檔案上傳PHP
- OkHttp使用+檔案的上傳+斷點續傳HTTP斷點
- 使用XML上傳檔案 (轉)XML
- JavaWeb之SpringMVC上傳檔案JavaWebSpringMVC
- Node.js 教程第九篇——Express 檔案上傳Node.jsExpress
- 檔案上傳之三基於flash的檔案上傳
- Http 檔案上傳進度為什麼不準HTTP
- 【SpringBoot】使用RestTemplate在服務之間進行MultipartFile格式檔案的傳遞【檔案上傳】Spring BootREST
- 單個檔案上傳和批量檔案上傳
- 使用SecureCRT上傳下載檔案Securecrt
- go的gin框架使用(六):檔案上傳Go框架
- Web 安全漏洞之檔案上傳Web
- Java Web之SpringMVC 上傳檔案JavaWebSpringMVC
- php圖片上傳之檔案安全PHP
- 檔案上傳
- PHP開發之檔案的上傳下載PHP
- 上傳檔案的陷阱
- 又拍雲 Node.js 實現檔案上傳、刪除Node.js
- node.js檔案傳輸框架Node.js框架