Maison > Article > interface Web > Nodejs express html5 implémente le glisser-déposer upload_node.js
1. Avant-propos
Le téléchargement de fichiers est une fonction relativement courante. La méthode de sélection traditionnelle est plus gênante. Vous devez d'abord cliquer sur le bouton de téléchargement, puis trouver le chemin du fichier, puis le télécharger. Cela pose de gros problèmes à l’expérience utilisateur. HTML5 commence à prendre en charge l'API requise pour le téléchargement par glisser-déposer. Nodejs est également une technologie de plus en plus populaire ces derniers temps. C'est aussi mon premier contact avec nodejs, l'un des frameworks de développement les plus couramment utilisés est expess, qui est un framework similaire au modèle mvc. Combinée avec HTML5 et nodejs express, la fonction de téléchargement par glisser-déposer est implémentée.
2. Vulgarisation des connaissances de base
1. Connaissance de base de NodeJs
Pour faire simple, nodejs est une plateforme de développement qui permet à js de s'exécuter sur le serveur. Nodejs se développe très rapidement et de nombreuses entreprises nationales ont commencé à l'utiliser, comme Taobao. Les plates-formes traditionnelles de développement d'applications Web s'appuient sur le multi-threading pour répondre aux demandes à forte concurrence. Nodejs adopte un modèle de conception monothread, asynchrone et piloté par les événements, ce qui apporte d'énormes améliorations de performances à nodejs. C'est également la plus grande fonctionnalité de nodejs. Dans nodejs, toutes les opérations d'E/S sont effectuées via des rappels. Lorsque nodejs effectue des opérations d'E/S, il envoie la demande d'E/S vers une file d'attente d'événements, attend que le programme la traite, attend que l'E/S le fasse. être traité, puis appeler La fonction de rappel renvoie le résultat.
Par exemple, lors de l'interrogation de la base de données, le fonctionnement est le suivant :
mysql.query("SELECT * FROM myTable",function(res){ callback(res); });
Dans le code ci-dessus, lorsque nodejs exécute l'instruction ci-dessus, il n'attendra pas que la base de données renvoie le résultat, mais continuera à exécuter les instructions suivantes. Une fois que la base de données a obtenu les données, elles seront envoyées à la file d'attente des boucles d'événements. Le rappel ne sera exécuté que lorsque le thread entrera dans la file d'attente des boucles d'événements.
Concernant plus de connaissances sur nodejs, je le lis depuis deux jours et je ne sais pas grand chose. Pour en savoir plus, vous pouvez effectuer une recherche sur Internet.
Introduction aux connaissances nodejs http://www.nodebeginner.org/index-zh-cn.html http://www.jb51.net/article/48755.htm
2. Connaissance de base de l'express
Nodejs est une communauté open source relativement active. Elle dispose d'un grand nombre de bibliothèques de développement tierces, parmi lesquelles Express est l'un des frameworks les plus complets et les plus couramment utilisés. C'est également le framework officiellement recommandé de nodejs. En plus d'encapsuler les opérations http courantes, il implémente également le contrôle du routage, la prise en charge de l'analyse des modèles, les vues dynamiques, les réponses des utilisateurs, etc. Mais ce n'est pas un framework universel. La plupart de ses fonctions sont de l'encapsulation http. Il s'agit simplement d'un framework léger. De nombreuses fonctions doivent également être intégrées à des bibliothèques tierces pour être implémentées.
Express fournit une prise en charge très pratique de la fonction de téléchargement. Après la demande de téléchargement de fichier, Express recevra le fichier et le stockera dans un répertoire temporaire. Ensuite, dans la méthode de routage, il suffit de copier le fichier à partir du répertoire temporaire. allez dans le dossier dans lequel nous souhaitons stocker les téléchargements des utilisateurs. Dans la partie téléchargement de fichiers, l'implémentation côté serveur est basée sur la fonction express.
3. API de téléchargement par glisser-déposer html5
HTML5 offre de nombreuses nouvelles fonctionnalités, les événements glisser-déposer et les téléchargements de fichiers sont l'une des nouvelles fonctionnalités. En raison de l'espace limité, nous nous concentrerons plus tard sur l'implémentation du code du téléchargement par glisser-déposer. Je ne listerai pas les applications de téléchargement par glisser-déposer fournies par HTML5 un par un. Si vous êtes intéressé, vous pouvez vous référer à : http://w3school.com.cn/html5/html5_ref_eventattributes.asp#Mouse_Events http://www.jb51.net/html5/85977.html
3. Implémentation du téléchargement par glisser-déposer1.Mise en œuvre du code
Regardons d'abord le répertoire de fichiers du front-end js :
Parmi eux :
uploader.js implémente principalement l'encapsulation de la fonction de téléchargement supportée par html5.
uploaderQueue.js implémente principalement la gestion des files d'attente de fichiers de téléchargement et des objets de téléchargement de fichiers, et télécharge les fichiers de la file d'attente de fichiers sur le serveur.
uploaderApp.js est le point d'entrée principal pour le téléchargement de fichiers. Il implémente principalement la surveillance des événements de glisser dans la fenêtre de téléchargement, place les fichiers glissés dans la file d'attente des fichiers de téléchargement et démarre le programme de téléchargement de fichiers.
Ce qui suit est une brève explication du code principal (obligatoire). Tous les codes peuvent être téléchargés ici : FileUploader
.
Tout d'abord, encapsulez simplement le téléchargement de fichier fourni par 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、实现效果
四、获取代码