Введение
В данные статье я хочу вам рассказать о моем способе загрузки файлов на сервер Node.js с помощью JQuery Ajax. Да, я понимаю что есть уже и другие решения, например JQuery File Upload, но все таки иногда хочется сделать что-то уже существующее, для того чтобы понять как это все устроено. Данное решение является учебным примером, все замечания по поводу кода или предложения по его улучшению оставляйте в комментариях.
Что будем использовать
Отправка файла с помощью Ajax
Все элементы на странице создаются вручную. За загрузку отвечает класс JSUploader вот один из его методов uploadFile:
this.uploadFile = function(index) { //baseClass это this var file = baseClass.allFiles[index]; //Создаем объек FormData var data = new FormData(); //Добавлем туда файл data.append('uploadFile', file.file); //отсылаем с попощью Ajax $.ajax({ url: '/', data: data, cache: false, contentType: false, processData: false, type: 'POST', success: function(response) { var message = file.element.find('td.message'); if(response.status == 'ok') { message.html(response.text); file.element.find('button.uploadButton').remove(); } else { message.html(response.errors); } }, xhr: function() { var xhr = $.ajaxSettings.xhr(); if ( xhr.upload ) { console.log('xhr upload'); xhr.upload.onprogress = function(e) { file.progressDone = e.position || e.loaded; file.progressTotal = e.totalSize || e.total; //обновляем прогресс для файла baseClass.updateFileProgress(index, file.progressDone, file.progressTotal, file.element); //обновляем общий прогресс baseClass.totalProgressUpdated(); }; } return xhr; } }); };
Обработка загрузки файлов
Для загрузки файлов на сервер нам понадобиться модуль multiparty, который можно установить с помощью команды в консоле:
npm install multipartyДалее код который обрабатывает post и get запросы начальной страницы. Здесь мы отображаем форму загрузки и обрабатываем post запрос на загрузку файла.
При окончание загрузки мы сообщаем клиенту что все хорошо или если есть ошибки, то отправить их. Единственная проблема по моему мнению — это то, что в данном коде сервер не может сразу завершить загрузку и сообщить об ошибки, для этого приходиться ждать полной загрузки файла. Решение проблемы возможно если мы как-нибудь вызовем сигнал onclose у формы, но к сожалению пока решения я не нашел.
var express = require('express'), router = express.Router(), fs = require("fs"), multiparty = require('multiparty'); //здесь выводим форму для загрузки router.get('/', function(req, res) { res.render('index', { title: 'Node.js File Uploads' }); }); //здесь происходит сама загрузка router.post('/', function(req, res, next) { // создаем форму var form = new multiparty.Form(); //здесь будет храниться путь с загружаемому файлу, его тип и размер var uploadFile = {uploadPath: '', type: '', size: 0}; //максимальный размер файла var maxSize = 2 * 1024 * 1024; //2MB //поддерживаемые типы(в данном случае это картинки формата jpeg,jpg и png) var supportMimeTypes = ['image/jpg', 'image/jpeg', 'image/png']; //массив с ошибками произошедшими в ходе загрузки файла var errors = []; //если произошла ошибка form.on('error', function(err){ if(fs.existsSync(uploadFile.path)) { //если загружаемый файл существует удаляем его fs.unlinkSync(uploadFile.path); console.log('error'); } }); form.on('close', function() { //если нет ошибок и все хорошо if(errors.length == 0) { //сообщаем что все хорошо res.send({status: 'ok', text: 'Success'}); } else { if(fs.existsSync(uploadFile.path)) { //если загружаемый файл существует удаляем его fs.unlinkSync(uploadFile.path); } //сообщаем что все плохо и какие произошли ошибки res.send({status: 'bad', errors: errors}); } }); // при поступление файла form.on('part', function(part) { //читаем его размер в байтах uploadFile.size = part.byteCount; //читаем его тип uploadFile.type = part.headers['content-type']; //путь для сохранения файла uploadFile.path = './files/' + part.filename; //проверяем размер файла, он не должен быть больше максимального размера if(uploadFile.size > maxSize) { errors.push('File size is ' + uploadFile.size + '. Limit is' + (maxSize / 1024 / 1024) + 'MB.'); } //проверяем является ли тип поддерживаемым if(supportMimeTypes.indexOf(uploadFile.type) == -1) { errors.push('Unsupported mimetype ' + uploadFile.type); } //если нет ошибок то создаем поток для записи файла if(errors.length == 0) { var out = fs.createWriteStream(uploadFile.path); part.pipe(out); } else { //пропускаем //вообще здесь нужно как-то остановить загрузку и перейти к onclose part.resume(); } }); // парсим форму form.parse(req); });
