一、前言
檔案上傳是比較常見的功能,傳統的選擇方式的上傳比較麻煩,需要先點選上傳按鈕,然後再找到檔案的路徑,然後上傳。給用戶體驗帶來很大問題。 html5開始支援拖曳上傳的需要的api。 nodejs也是最近越來越流行的技術,這也是自己第一次接觸nodejs,在nodejs開發中,最常用的開發框架之一是expess,它是一個類似mvc模式的框架。結合html5、nodejs express實現了拖曳上傳的功能。
二、基礎知識普及
1、NodeJs基礎
nodejs簡單來說就是一個可以讓js在服務端也能運作的開發平台,nodejs發展非常很快,許多國內公司也已經開始使用例如淘寶等。傳統的web應用程式開發平台依靠多執行緒來實現高並發請求的回應。而nodejs採用了單執行緒、非同步式IO、事件驅動的設計模型,為nodejs帶來了巨大的效能提升。這也是nodejs最大的特點,在nodejs中,所有的IO操作都是透過回呼的方式進行,nodejs在執行IO操作時會把IO請求推送一個事件隊列,等待程式處理,等待處理完IO,然後調用回調函數傳回結果。
例如在查詢資料庫操作如下:
mysql.query("SELECT * FROM myTable",function(res){ callback(res); });
在上述程式碼中,nodejs在執行上述語句時,不會等待資料庫回傳結果,而是繼續執行後面的語句。在資料庫取得到資料後,會傳送到事件循環佇列中,等到執行緒進入事件循環佇列後,才執行callback的東西。
關於nodejs更多的知識,我也知識看了兩天,了解不多。了解更多的知識可以在網路上搜尋。
nodejs入門的知識 http://www.nodebeginner.org/index-zh-cn.html http://www.jb51.net/article/48755.htm
2、express基礎
nodejs是一個比較活躍的開源社區,它擁有大量的第三方開發庫,其中Express是其中最廣泛的、最常用的框架之一。也是nodejs官方推薦的框架。除了對常見http操作的封裝,它也實現了路由控制、模版解析支援、動態試圖、使用者回話等等。但它也不是萬能的框架,絕大多數功能是對http的封裝,它只是一個輕量級的框架。很多功能還需要整合第三方函式庫還實現。
exress提供了非常方便的上傳功能的支持,在文件上傳請求以後,express會接收文件並把文件存在一個臨時目錄,然後在路由到的方法中,我們只需把文件從臨時目錄下拷貝到我們要存放用戶上傳資料夾即可。在檔案上傳部分,伺服器端的實作就是基於express這個功能來實現的。
3、html5拖曳上傳api
html5提供許多新的特性,拖曳事件以及檔案上傳就是新功能之一。由於篇幅有限,後面重點介紹拖曳上傳的程式碼實作。就不一一列出html5提供的拖曳上傳的apil了,有興趣的可以參考: http://w3school.com.cn/html5/html5_ref_eventattributes.asp#Mouse_Events http:/ /www.jb51.net/html5/85977.html
三、拖曳上傳實作
1、程式碼實作
先來看下前端js的檔案目錄:
其中:
uploader.js主要實作對html5支援的上傳功能的封裝。
uploaderQueue.js主要實作上傳檔案佇列的管理,以及檔案上傳對象,把檔案佇列中的檔案上傳到伺服器。
uploaderApp.js主要檔案上傳的入口,主要實作上傳視窗對拖曳事件的監聽並把拖曳檔案推進上傳檔案佇列,啟動檔案上傳程式。
下面對核心程式碼(需要)做簡單的解釋,全都程式碼可以到這裡下載: FileUploader
先對html5提供的檔案上傳做簡單的封裝uploader.js
function uploader(url, data, files) { this._files = files; this._data = data; this._url = url; this._xhr = null; this.onloadstart = {}; this.onload = {}; this.onloadend = {}; this.onprogress = {}; this.onerror = {}; this.ontimeout = {}; this.callback = {};//请求完成后回调 _self = this; } uploader.prototype = { init: function () { if (!isValid()) { throw e; } this._xhr = new XMLHttpRequest(); this._bindEvents(); }, send: function () { if (this._xhr == null) { this.init(); } var formData = this._createFormData(); this._xhr.open('post', this._url, true); this._xhr.send(formData); }, _bindEvents: function () { _self = this; this._xhr.upload.loadstart = function (e) { evalFunction(_self.onloadstart, e); } this._xhr.upload.onload = function (e) { evalFunction(_self.onload, e); }; this._xhr.upload.onloadend = function (e) { evalFunction(_self.onloadend, e); } this._xhr.upload.onprogress = function (e) { evalFunction(_self.onprogress, e) }; this._xhr.upload.onerror = function (e) { evalFunction(_self.onerror, e); }; this._xhr.upload.ontimeout = function (e) { evalFunction(_self.ontimeout, e); } this._xhr.onreadystatechange = function () { if (_self._xhr.readyState == 4) { if (typeof _self.callback === 'function') { var status = _self._xhr.status; var data = _self._xhr.responseText; _self.callback(status, data); } } } }, _createFormData: function () { var formData = new FormData(); this._addDataToFormData(formData); this._addFileToFormData(formData); return formData; }, _addDataToFormData: function (formData) { if (this._data) { for (var item in this._data) { formData.append(item, this._data[item]); } } }, _addFileToFormData: function (formData) { if (this._files) { for (var i = 0; i < this._files.length; i++) { var file = this._files[i]; formData.append('file[' + i + ']', this._files[i]); } } } }; View Code var uploaderFactory = { send: function (url, data, files, callback) { var insUploader = new uploader(url, data, files); insUploader.callback = function (status, resData) { if (typeof callback === 'function') { callback(status, resData); } } insUploader.send(); return insUploader; } };
uploader对象主要是对html5提供的原生api进行简单的封装。uploaderFactory提供一个简单的接口,使用它可以像jquery的ajax方法一样完成,文件上传调用。html5中提供的文件上传的支持,是在原来XMLHttpRequest基础之上扩展一些属性和方法,提供了FormData对象,来支持文件上传操作。
文件上传队列(uploaderQueue.js)也是一个比较重要的对象,它包括两个对象一个是Queue,文件队列对象,主要负责管理文件队列的增删改查询等操作,另一个对象是UploadEngine,文件上传引擎,它的功能主要是负责从文件队列中取出文件对象,调用uploader对象上传文件,然后更新文件队列中的文件状态。Queue以及UploadEngine都是单例对象。
首先来看下文件队列对象:
(function (upladerQueue) { var Status = { Ready: 0, Uploading: 1, Complete: 2 } var _self = null; var instance = null; function Queue() { this._datas = []; this._curSize = 0;//当前长度 _self = this; } Queue.prototype = { add: function (data) { var key = new Date().getTime(); this._datas.push({key: key, data: data, status: Status.Ready}); this._curSize = this._datas.length; return key; }, remove: function (key) { var index = this._getIndexByKey(key); this._datas.splice(index, 1); this._curSize = this._datas.length; }, get: function (key) { var index = this._getIndexByKey(key); return index != -1 ? this._datas[index].data : null; }, clear: function () { this._datas = []; this._curSize = this._datas.length; }, size: function () { return this._curSize; }, setItemStatus: function (key, status) { var index = this._getIndexByKey(key); if (index != -1) { this._datas[index].status = status; } }, nextReadyingIndex: function () { for (var i = 0; i < this._datas.length; i++) { if (this._datas[i].status == Status.Ready) { return i; } } return -1; }, getDataByIndex: function (index) { if (index < 0) { return null; } return this._datas[index]; }, _getIndexByKey: function (key) { for (var i = 0; i < this._datas.length; i++) { if (this._datas[i].key == key) { return i; } } return -1; } }; function getInstace() { if (instance === null) { instance = new Queue(); return instance; } else { return instance; } } upladerQueue.Queue = getInstace(); upladerQueue.UploadStatus = Status; })(window.uploaderQueue);
上传文件队列使用一个数组管理每个文件对象信息,每个文件对象有key,data,status三个属性,该对象主要负责文件对象的增加、删除、更新、查找的功能。
上传文件队列中另一个比较重要的对象是上传引擎对象(uploadEngine.js)
(function (upladerQueue) { var instance = null; var _self; function uploadEngine() { this._url = null; this._curUploadingKey = -1;//标志 this.uploadStatusChanged = {}; this.uploadItemProgress={}; _self = this; } uploadEngine.prototype = { setUrl: function (url) { this._url = url; }, run: function () { if (this._curUploadingKey === -1 && this._url) { this._startUpload(); } }, _startUpload: function () { _self = this; var index = upladerQueue.Queue.nextReadyingIndex(); if (index != -1) { this._uploadItem(index); } else { this._curUploadingKey = -1; return null; } }, _uploadItem: function (index) { var data = upladerQueue.Queue.getDataByIndex(index).data; _self = this; this._readyUploadItem(index); var upload = uploaderFactory.send(this._url, null, data.files, function (status, data) { _self._completedUploadItem.call(_self, status, data); }); this._uploadItemProgress(upload); }, _uploadItemProgress: function (upload) { upload.onprogress = function (e) { _self.uploadItemProgress(_self._curUploadingKey,e); } }, _readyUploadItem: function (index) { this._curUploadingKey = upladerQueue.Queue.getDataByIndex(index).key; if (typeof this.uploadStatusChanged === 'function') { this.uploadStatusChanged(this._curUploadingKey, upladerQueue.UploadStatus.Uploading); } upladerQueue.Queue.setItemStatus(this._curUploadingKey, upladerQueue.UploadStatus.Uploading); }, _completedUploadItem: function (status, data) { if (typeof this.uploadStatusChanged === 'function') { this.uploadStatusChanged(this._curUploadingKey, upladerQueue.UploadStatus.Complete); } upladerQueue.Queue.setItemStatus(this._curUploadingKey, upladerQueue.UploadStatus.Complete); this._startUpload(); } }; function getInstace() { if (instance === null) { instance = new uploadEngine(); } return instance; } upladerQueue.Engine = getInstace(); })(window.uploaderQueue);
该对象比较简单主要提供一个run以及setUrl方法,用于启动上传引擎,以及设置上传路径的功能。内部使用递归的方法把文件队列中的方法全部上传到服务端。使用uploadItemProgress通知外部上传的进度,使用uploadStatusChanged通知文件上传状态,以便更新UI.
uploaderApp.js中主要包括三个对象,一个是类似jquery的一个简单的jquery对象(App$)。主要用于绑定事件。一个是uploaderArea对象,是拖曳上传的窗口区域,另一个是入口对象uploaderMain对象。主要用于初始化对象,对外部提供一个init方法,来初始化整个对象。
了解关于App$以及uploaderArea对象的代码请下载 源代码 ,下面仅对uploaderMain对象做简单的说明。
(function (app) { var _self; function uploaderMain(id) { this._id = id; this._area = null; this.uploaders = []; this._URL = 'file/uploader'; } uploaderMain.prototype = { init: function () { _self = this; this._initArea(); this._initQueueEng(); }, _initQueueEng: function () { uploaderQueue.Engine.setUrl(this._URL); uploaderQueue.Engine.uploadStatusChanged = function (key, status) { if (status === uploaderQueue.UploadStatus.Uploading) { _self._area.hideItemCancel(key); } else if (status === uploaderQueue.UploadStatus.Complete) { _self._area.completeItem(key); _self._area.showItemCancel(key); } } uploaderQueue.Engine.uploadItemProgress = function (key, e) { var progress = e.position / e.total; _self._area.changeItemProgress(key, Math.round(progress * 100)); } }, _initArea: function () { this._area = new app.area(this._id); this._area.init(); this._area.drop = function (e) { var key = uploaderQueue.Queue.add({files: e.dataTransfer.files}); uploaderQueue.Engine.run(); return key; } this._area.cancelItem = function (key) { uploaderQueue.Queue.remove(key); } } }; app.main = uploaderMain; })(window.uploaderApp);
在uploaderMain对象,相当于各个对象之间的中介,主要就是做对象的初始化功能、以及对象之间相互调用。使各个对象之间相互协作完成整个模块的功能。对外提供一个init方法来初始化整个程序,在html页面中只需如下代码:
<script type="text/javascript"> var main=new uploaderApp.main('container'); main.init(); </script>
以上代码就是创建一个入口对象,然后使用init方法来启动整个程序。
以上是对前端js的主要方法做的简单解释,如果想详细了解请下载源代码。下面简单看下后端js(nodejs)端实现的主要代码。
在express基础知识时,已经讲过在express已经对文件上传功能做了完整的封装,当路由到action时,文件已经完成上传只是文件上传到了一个临时目录,这个临时目录我们可以在app.js中配置的,配置方式如下:
app.use(express.bodyParser({ uploadDir:__dirname+'/public/temp' }));
这样在文件上传后文件就存放在/public/temp目录下,文件名也是express通过一定的算法随机获取的。在我们写的action中只需要把存在临时目录中的文件移动到服务端存放文件的目录下,然后删除临时目录下的文件即可。具体代码如下:
function uploader(req, res) { if (req.files != 'undifined') { console.dir(req.files); utils.mkDir().then(function (path) { uploadFile(req, res, path, 0); }); } } function uploadFile(req, res, path, index) { var tempPath = req.files.file[index].path; var name = req.files.file[index].name; if (tempPath) { var rename = promise.denodeify(fs.rename); rename(tempPath, path + name).then(function () { var unlink = promise.denodeify(fs.unlink); unlink(tempPath); }).then(function () { if (index == req.files.file.length - 1) { var res = { code: 1, des: '上传成功' }; res.send(res); } else { uploadFile(req, res, path, index + 1); } }); } }
2、实现效果
四、获取代码

Python和JavaScript在社區、庫和資源方面的對比各有優劣。 1)Python社區友好,適合初學者,但前端開發資源不如JavaScript豐富。 2)Python在數據科學和機器學習庫方面強大,JavaScript則在前端開發庫和框架上更勝一籌。 3)兩者的學習資源都豐富,但Python適合從官方文檔開始,JavaScript則以MDNWebDocs為佳。選擇應基於項目需求和個人興趣。

從C/C 轉向JavaScript需要適應動態類型、垃圾回收和異步編程等特點。 1)C/C 是靜態類型語言,需手動管理內存,而JavaScript是動態類型,垃圾回收自動處理。 2)C/C 需編譯成機器碼,JavaScript則為解釋型語言。 3)JavaScript引入閉包、原型鍊和Promise等概念,增強了靈活性和異步編程能力。

不同JavaScript引擎在解析和執行JavaScript代碼時,效果會有所不同,因為每個引擎的實現原理和優化策略各有差異。 1.詞法分析:將源碼轉換為詞法單元。 2.語法分析:生成抽象語法樹。 3.優化和編譯:通過JIT編譯器生成機器碼。 4.執行:運行機器碼。 V8引擎通過即時編譯和隱藏類優化,SpiderMonkey使用類型推斷系統,導致在相同代碼上的性能表現不同。

JavaScript在現實世界中的應用包括服務器端編程、移動應用開發和物聯網控制:1.通過Node.js實現服務器端編程,適用於高並發請求處理。 2.通過ReactNative進行移動應用開發,支持跨平台部署。 3.通過Johnny-Five庫用於物聯網設備控制,適用於硬件交互。

我使用您的日常技術工具構建了功能性的多租戶SaaS應用程序(一個Edtech應用程序),您可以做同樣的事情。 首先,什麼是多租戶SaaS應用程序? 多租戶SaaS應用程序可讓您從唱歌中為多個客戶提供服務

本文展示了與許可證確保的後端的前端集成,並使用Next.js構建功能性Edtech SaaS應用程序。 前端獲取用戶權限以控制UI的可見性並確保API要求遵守角色庫

JavaScript是現代Web開發的核心語言,因其多樣性和靈活性而廣泛應用。 1)前端開發:通過DOM操作和現代框架(如React、Vue.js、Angular)構建動態網頁和單頁面應用。 2)服務器端開發:Node.js利用非阻塞I/O模型處理高並發和實時應用。 3)移動和桌面應用開發:通過ReactNative和Electron實現跨平台開發,提高開發效率。

JavaScript的最新趨勢包括TypeScript的崛起、現代框架和庫的流行以及WebAssembly的應用。未來前景涵蓋更強大的類型系統、服務器端JavaScript的發展、人工智能和機器學習的擴展以及物聯網和邊緣計算的潛力。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

PhpStorm Mac 版本
最新(2018.2.1 )專業的PHP整合開發工具

WebStorm Mac版
好用的JavaScript開發工具

Atom編輯器mac版下載
最受歡迎的的開源編輯器

Dreamweaver Mac版
視覺化網頁開發工具