Node.js - это среда выполнения JavaScript с открытым исходным кодом для разработки различных серверных инструментов и приложений, обычно используемых для создания backend apis. В сегодняшнем руководстве мы собираемся обрабатывать загрузку файлов на сервер nodejs с помощью экспресс-фреймворка. Мы продолжим работу с API входа и регистрации, которые мы создали в предыдущей статье, которые можно найти здесь.

Мы собираемся использовать промежуточное программное обеспечение multer для обработки загрузки файлов, асинхронный модуль для обработки файловых операций и node cmd в случае, если вам нужно запускать команды терминала в nodejs. Итак, давайте установим их, выполнив следующую команду:

npm install --save multer async node-cmd

Затем отредактируйте файл server.js следующим образом:

var express    = require("express");
var login = require('./routes/loginroutes');
var upload = require('./routes/fileroutes');
var bodyParser = require('body-parser');
/*
Module:multer
multer is middleware used to handle multipart form data
*/
var multer = require('multer');
var multerupload = multer({ dest: 'fileprint/' })
var app = express();
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(function(req, res, next) {
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
    next();
});
var router = express.Router();
// test route
router.get('/', function(req, res) {
    res.json({ message: 'welcome to our upload module apis' });
});
//route to handle user registration
router.post('/register',login.register);
router.post('/login',login.login);
//route to handle file printing and listing
router.post('/fileupload',multerupload.any(),upload.fileupload);
app.use('/api', router);
app.listen(4000);

Мы импортировали модуль multer в server.js и установили место назначения временного хранилища файлов в строках 9 и 10. Затем мы создали маршрут загрузки файлов, который будет обрабатывать запросы загрузки файлов на сервер. Мы установили any опция в мультизагрузке, позволяющая загружать любые типы файлов. Вы можете найти другие параметры для конкретных файлов здесь. Теперь я создал еще один файл маршрутов с именем fileroutes.js в папке маршрутов для обработки загрузки файлов и перечисления задач и вызвал функцию загрузки файлов из этого файла в строке 31. Файловая структура теперь выглядит следующим образом:

├── package.json
├── маршруты
│ ├── fileroutes.js
│ └── loginroutes.js
├── server.js

Теперь предположим, что пользователь отправляет несколько файлов из внешнего интерфейса одновременно в виде массива. Давайте теперь перейдем к определению обработчика загрузки файла в файле fileroutes.js.

var fs = require("fs");
var path = require('path');
var async = require('async');
var cmd=require('node-cmd');
exports.fileprint = function(req,res){
  // console.log("req",req.files);
  var filesArray = req.files;
        async.each(filesArray,function(file,eachcallback){
         //carry out your file operations here and pass callback at the end  
         },function(err){
          if(err){
              console.log("error ocurred in each",err);
          }
          else{
            console.log("finished prcessing");
            res.send({
                      "code":"200",
                      "success":"files printed successfully"
                      })
            cmd.run('rm -rf ./fileupload/*');
          }
          });
}

В этом файле мы собираемся сначала хранить файлы, полученные из клиентского запроса, в локальной переменной filesArray.

Если вы распечатаете консольный вывод req.files, он будет иметь следующий формат:

[ { fieldname: 'bigcalender.png',
    originalname: 'bigcalender.png',
    encoding: '7bit',
    mimetype: 'image/png',
    destination: 'fileupload/',
    filename: '36d838a63e8df1f38bd94ca93fd83a9d',
    path: 'fileupload/36d838a63e8df1f38bd94ca93fd83a9d',
    size: 39568 } ]

Метод async.each принимает массив в качестве входных данных, выполняет итерацию по каждому элементу, а затем использует обратный вызов для выполнения обработки после того, как все элементы были повторены (более подробное объяснение здесь). Мы перебираем массив, используя метод async each, поскольку он позволяет нам получить окончательный обратный вызов после того, как все файлы в массиве завершат обработку. В нашем случае мы отправили ответ клиенту в окончательном обратном вызове и удалили временные файлы, созданные в папке загрузки файлов с помощью модуль multer.

Продвигаясь дальше, мы можем хранить файлы по определенному пути, используя модуль fs в async each модуле следующим образом:

async.each(filesArray,function(file,eachcallback){
        async.waterfall([
        function (callback) {
          fs.readFile(file.path, (err, data) => {
            if (err) {
              console.log("err ocurred", err);
              }
            else {
              callback(null,data);
            }
            });
        },
        function (data, callback) {
          var writepath = "/home/user/Documents";
          fs.writeFile(writePath + file.originalname, data, (err) => {
          if (err) {
            console.log("error occured", err);
          }
          else {
          callback(null, 'done');
          }
          });
        }
        ], function (err, result) {
          // result now equals 'done'
          //pass final callback to async each to move on to next file
          eachcallback();
        });
        },function(err){
          if(err){
              console.log("error ocurred in each",err);
          }
          else{
            console.log("finished prcessing");
            ...
          }
          });

В этом случае мы использовали метод асинхронного водопада в async.each для синхронного выполнения ряда задач. Метод асинхронного водопада принимает массив функций в качестве входных данных, запускает их одну за другой и обрабатывает окончательный обратный вызов после того, как все функции завершили выполнение синхронно. Следует иметь в виду, что операция async.each выполняет итерацию по элементам массива параллельно, в то время как водопад гарантирует, что каждая параллельная итерация сначала будет читать файл из пути к файлу, а затем файл записывается в путь записи. Проще говоря, даже если вы передадите на сервер одни и те же 10 файлов несколько раз, порядок, в котором они будут обрабатываться, будет каждый раз отличаться, однако каждый раз сервер сначала будет читать данные файла, а затем записывать их в путь к файлу.

Вы можете найти полный код fileroutes.js в github gist, загруженном здесь.

Бонусный совет:

Вы можете изменить метод async.waterfall для хранения файлов в базе данных или хранилище сервера.

Я написал учебник с использованием reactjs для создания простого веб-сайта, который отправляет файлы на сервер узла, который можно найти здесь.

Подключитесь глубже:

В следующем уроке я расскажу, как сохранять и извлекать файлы в облачное хранилище в nodejs с помощью openstack и docker, поэтому следите за нашей страницей facebook, чтобы получать уведомления о подобных публикациях в будущем: Technoetics или просто следите за нашей публикацией на носителе.