1. Preface
File upload is a relatively common function. The traditional method of uploading is more troublesome. You need to click the upload button first, then find the path to the file, and then upload it. It brings great problems to the user experience. html5 starts to support drag and drop the required API for uploading. nodejs is also a technology that has become increasingly popular recently. This is also my first time to get involved in nodejs. In nodejs development, one of the most commonly used development frameworks is Expess, which is a framework similar to the mvc pattern. Combined with html5 and nodejs express, the function of drag and drop upload is realized.
2. Popularization of basic knowledge
1. Basic knowledge of NodeJs
To put it simply, nodejs is a development platform that allows js to run on the server side. Nodejs is developing very fast, and many domestic companies have also started to use it, such as Taobao. Traditional web application development platforms rely on multi-threading to achieve high-concurrent request responses. nodejs adopts a single-threaded, asynchronous IO, and event-driven design model, which brings huge performance improvements to nodejs. This is also the biggest feature of nodejs. In nodejs, all IO operations are carried out through callbacks. When nodejs executes IO operations, it will push the IO request to an event queue, wait for the program to process it, and then call the callback function to return the result.
For example, when querying the database, the operation is as follows:
mysql.query("SELECT * FROM myTable",function(res){ callback(res); });In the above code, when nodejs executes the above statement, it will not wait for the database to return the result, but will continue to execute the following statement. After the database obtains data, it will be sent to the event loop queue. The callback thing will be executed only after the thread enters the event loop queue.
I have read more about nodejs for two days and don’t know much. To learn more, you can search on the Internet.
Knowledge about getting started with nodejs http://www.nodebeginner.org/index-zh-cn.html //www.VeVB.COM/article/48755.htm
2. Basic knowledge of express
nodejs is a relatively active open source community with a large number of third-party development libraries, among which Express is one of the most extensive and most commonly used frameworks. It is also the official framework recommended by nodejs. In addition to encapsulation of common http operations, it also implements routing control, template resolution support, dynamic attempts, user reply, etc. But it is not a omnipotent framework either. Most of the functions are encapsulation of http, it is just a lightweight framework. Many functions also need to be integrated into third-party libraries and implemented.
Exress provides support for very convenient upload functions. After the file upload request, Express will receive the file and store the file in a temporary directory. Then in the routing method, we just need to copy the file from the temporary directory to the folder we want to store the user upload. In the file upload part, the server-side implementation is based on the express function.
3. html5 drag and upload api
HTML5 provides many new features, drag events and file upload are one of the new features. Due to limited space, the implementation of drag-up uploaded code will be highlighted later. I won't list the drag uploaded api provided by html5 one by one. If you are interested, please refer to: http://w3school.com.cn/html5/html5_ref_eventattributes.asp#Mouse_Events //www.VeVB.COM/html5/85977.html
3. Drag and upload to achieve
1. Code implementation
Let’s first look at the file directory of the front-end js:
in:
uploader.js mainly implements the encapsulation of the upload function supported by html5.
uploaderQueue.js mainly implements the management of uploading file queues and file upload objects, uploading files in the file queue to the server.
The main entrance to uploaderApp.js file upload, which mainly implements the upload window to listen to drag events and pushes the dragged files into the upload file queue, and starts the file upload program.
The following is a simple explanation of the core code (needed). All the codes can be downloaded here: FileUploader
First, simply encapsulate uploader.js for uploading files provided by html5
function uploader(url, data, files) { this._files = files; this._data = data; this._url = url; this._xhr = null; this.onloadstart = {}; this.onloadend = {}; this.onprogress = {}; this.onerror = {}; this.ontimeout = {}; this.callback = {};//Callback after the request is completed_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 Codevar 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; } };The uploader object mainly simply encapsulates the native API provided by html5. uploaderFactory provides a simple interface, which can be done like jquery's ajax method, file upload calls. The support for file upload provided in html5 is to extend some properties and methods based on the original XMLHttpRequest, and provide FormData objects to support file upload operations.
The file upload queue (uploaderQueue.js) is also a relatively important object. It includes two objects, one is Queue, file queue object, which is mainly responsible for managing the addition, deletion, modification and query of file queues. The other object is UploadEngine, a file upload engine. Its function is mainly responsible for taking out file objects from the file queue, calling the uploader object to upload files, and then updating the file status in the file queue. Queue and UploadEngine are singleton objects.
First, let’s take a look at the file queue object:
(function (upladerQueue) { var Status = { Ready: 0, Uploading: 1, Complete: 2 } var _self = null; var instance = null; function Queue() { this._datas = []; this._curSize = 0;//Current length_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; } }, nextReadingIndex: 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 = getInstance(); upladerQueue.UploadStatus = Status;})(window.uploaderQueue);The upload file queue uses an array to manage the information of each file object. Each file object has three attributes: key, data, and status. This object is mainly responsible for the functions of adding, deleting, updating and searching file objects.
Another more important object in the upload file queue is the upload engine object (uploadEngine.js)
(function (upladerQueue) { var instance = null; var _self; function uploadEngine() { this._url = null; this._curUploadingKey = -1;//Flags 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);This object is relatively simple and mainly provides a run and setUrl method, which is used to start the upload engine and set the upload path. The recursive method is used internally to upload all methods in the file queue to the server. Use uploadItemProgress to notify the progress of external uploads, and use uploadStatusChanged to notify file upload status to update the UI.
uploaderApp.js mainly includes three objects, one is a simple jquery object (App$) similar to jquery. Mainly used for binding events. One is the uploaderArea object, which is the window area that is dragged and uploaded, and the other is the entrance object, the uploaderMain object. It is mainly used to initialize objects, providing an init method to the outside to initialize the entire object.
To learn about the code about App$ and uploaderArea objects, please download the source code. The following is only a brief explanation of the uploaderMain object.
(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);The uploaderMain object is equivalent to the intermediary between each object, mainly to make the initialization function of the object and call each other between objects. Make each object collaborate with each other to complete the functions of the entire module. Provide an init method to initialize the entire program. In the html page, just need the following code:
<script type="text/javascript"> var main=new uploaderApp.main('container'); main.init();</script>The above code is to create an entry object and then use the init method to start the entire program.
The above is a simple explanation of the main methods of front-end js. If you want to know more, please download the source code. Let’s take a look at the main code of the backend js (nodejs) side implementation.
In the basics of express, we have already said that the file upload function has been completely encapsulated in express. When routing to action, the file has been uploaded only if the file is uploaded to a temporary directory. We can configure this temporary directory in app.js. The configuration method is as follows:
app.use(express.bodyParser({ uploadDir:__dirname+'/public/temp'}));In this way, after the file is uploaded, the file is stored in the /public/temp directory, and the file name is also randomly obtained by express through certain algorithms. In the action we write, we only need to move the files in the temporary directory to the directory where the files are stored on the server, and then delete the files in the temporary directory. The specific code is as follows:
function uploader(req, res) { if (req.files != 'undifened') { 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: 'Uploaded successfully' }; res.send(res); } else { uploadFile(req, res, path, index + 1); } }); }}2. Realize the effect
4. Get the code
Code download address: //www.VeVB.COM/jiaoben/202117.html