Чтение содержимого файла csv в node.js

Я пытаюсь реализовать модуль в nodejs (только что начал работать в nodejs), который имеет требование ниже, как

  1. Загрузите CSV-файл.
  2. Прочитать содержимое CSV-файла.

Фреймворки, которые в настоящее время используются для restful API, — это «express»: «~ 4.2.0» и multer для загрузки файлов.

Теперь я настроил multer, как показано ниже, в моем app.js.

app.use(multer({
  onFileUploadData : function(file, data){
    console.log('onFileUploadData Called with data - '+ data);
  }
}));

В моем файле маршрута у меня есть конечная точка сообщения, как показано ниже.

app.post('/sample.csv',lead.processCSV);

Этот маршрут вызывается из вызова ajax ниже, как

$.ajax({
            xhrFields: {withCredentials: true},
            url: '/sample.csv',
            type: 'POST',
            success: function (data) {
                $scope.handleResponse(data);
            },
            error: function (error, xhr) {
                angular.element('#csvUploadBusyIcon').hide();
                alert('Oops! Upload failed');
            },
            data: formData,
            cache: false,
            contentType: false,
            processData: false
        });

Теперь я хочу получить содержимое CSV-файла, т.е. когда все содержимое будет загружено, я должен обработать свой метод lead.processCSV.

Также мне нужен какой-либо другой модуль для файлов csv, или в моем случае достаточно multer?

Любое предложение / руководство в правильном направлении будет полезно. Заранее спасибо.


person Mozak    schedule 03.10.2014    source источник


Ответы (2)


Есть замечательный проект node, который мне очень помог. Вы должны проверить это Мы собираемся использовать их модуль csv-parse. Он может получать поток в качестве входных данных и читать его построчно, не блокируя цикл обработки событий, поэтому в основном, когда вы обрабатываете файл, ваш сервер не будет зависать, а другие запросы все еще могут нормально обрабатываться.

Поскольку вы сказали, что только начинаете работать с nodejs, вам следует быстро поискать и понять, как промежуточное программное обеспечение работает в процессе обработки запросов. Для упрощения обработки запросов промежуточное ПО представляет собой функцию (req, res, next). С req вы получаете данные запроса. С res вы можете отправить ответ, а затем вы отправляете свои объекты req и res следующему промежуточному программному обеспечению. Таким образом, вы можете обрабатывать запрос по частям, и последнее промежуточное ПО потока отправит ответ клиенту (например, res.send(200)).

вызов Multer({...}) возвращает промежуточную функцию. Когда запрос поступает к этому промежуточному ПО, multer попытается загрузить любые файлы, отправленные пользователем в почтовом запросе. Когда вы говорите app.use(Multer({...})), вы просите multer попытаться загрузить файлы из ЛЮБЫХ почтовых запросов, содержащих файлы. Это угроза безопасности, если не все ваши маршруты ожидают загрузки файлов.

Хорошо, как говорится, вот пример кода, который я написал для вашего варианта использования:

//Important Security advice: 
//don't add multer as a middleware to all requests. 
//If you do this, people will be able to upload files
//in ALL YOUR 'post' handlers!!! 

var Multer = require('multer');
var Parse = require('csv-parse');
var fs = require('fs')

function parseCSVFile(sourceFilePath, columns, onNewRecord, handleError, done){
    var source = fs.createReadStream(sourceFilePath);

    var linesRead = 0;

    var parser = Parse({
        delimiter: ',', 
        columns:columns
    });

    parser.on("readable", function(){
        var record;
        while (record = parser.read()) {
            linesRead++;
            onNewRecord(record);
        }
    });

    parser.on("error", function(error){
        handleError(error)
    });

    parser.on("end", function(){
        done(linesRead);
    });

    source.pipe(parser);
}

//We will call this once Multer's middleware processed the request
//and stored file in req.files.fileFormFieldName

function parseFile(req, res, next){
    var filePath = req.files.file.path;
    console.log(filePath);
    function onNewRecord(record){
        console.log(record)
    }

    function onError(error){
        console.log(error)
    }

    function done(linesRead){
        res.send(200, linesRead)
    }

    var columns = true; 
    parseCSVFile(filePath, columns, onNewRecord, onError, done);

}

//this is the route handler with two middlewares. 
//First:  Multer middleware to download file. At some point,
//this middleware calls next() so process continues on to next middleware
//Second: use the file as you need

app.post('/upload', [Multer({dest:'./uploads'}), parseFile]);

Надеюсь, это помогло. Убедитесь, что понимаете, как промежуточное программное обеспечение маршрутов работает в узле: они являются ключом к хорошему качеству кода.

Марсель

person Marcel Batista    schedule 17.10.2014
comment
Спасибо за ответ, Марсель, есть ли что-то, что мы можем обрабатывать CSV-файлы без какого-либо пакета? это займет много времени? - person Ilyas karim; 19.10.2018

У меня был аналогичный запрос на обработку CSV-файла, и я попытался реализовать ваше решение: оно работает, но пока я использовал его с журналом консоли. Я попытался сохранить переменную «запись» в массиве с именем «результаты», но я только что получил пустой массив [], и после представления этого пустого массива я получил ответ console.log, представляющий проанализированные данные CSV.

Так что, похоже, это вопрос синхронизации. Я имею в виду, что обработка CSV-файла занимает некоторое время. Поэтому я попытался сжать ваш код и преобразовать его в обещание, а затем выполнить его. Итак, после выполнения обещания мой массив был готов к использованию.

  1. Примечание: я новичок, поэтому могут быть ошибки. До сих пор это работает нормально для меня.
  2. Примечание. Содержимое моего тестового CSV-файла:
title, type, value, category
Loan, income, 1500, Others
Website Hosting, outcome, 50, Others
Ice cream, outcome, 3, Food
  1. Примечание: есть некоторые отличия от вашего случая: я получаю один единственный файл из каталога '/import. Я использую приложение Insomnina Designer для отправки тела составной формы с одним файлом с именем importFile.

  2. Примечание. Я импортировал те же библиотеки, что и вы, а также использовал концепцию промежуточного программного обеспечения.

  3. Примечание. В этом случае я просто ожидал один файл, поэтому использовал multer({dest: './upload'}).single('importFile'). Его также можно использовать .any().

  4. Примечание. Я использую машинописный текст, поэтому для JS это просто вопрос удаления после объявления некоторой переменной :@type, например

  5. Примечание: я оставил вариант 1 — работа только с массивами и вариант 2 — использование объектов.

const results: object[] = [];
becomes:
const results = [];

Переходим к коду:

import { Router, Request, Response } from 'express';
import csv from 'csv-parse';
import multer from 'multer';
import fs from 'fs';

// used on option 2 due typescript
interface CSVTransactionDTO {
  title: string;
  value: number;
  type: 'income' | 'outcome';
  category: string;
}

app.post(
  '/import', // route name
  multer({ dest: './upload' }).single('importFile'), // middleware to download one file (csv)
  async (request: Request, response: Response) => {//last middleware with CSV parsing with arrow function
    const filePath = request.file.path;
    
    
let rowCounter = 0;
    const results: string[] = [];// option 1
    const newTransactions: CSVTransactionDTO[] = [];// option 2
    
    function parseCSVPromise(): Promise<void> {
      return new Promise((resolve, reject) => {
        const ConfigCSV = {
          // delimiter:';',//other delimiters different from default = ','
          from_line: 2, // data starts here
          trim: true, // ignore white spaces immediately around the delimiter (comma)
        };

        fs.createReadStream(filePath)
          .pipe(csv(ConfigCSV))
          .on('data', /* async */ row => {
            rowCounter += 1;// counter of how many rows were processed
            // console.log(data); // just test
            results.push(row); // Option1 - The simplest way is to push a complete row

            const [title, type, value, category] = row;// Option2, process it as an object
            newTransactions.push({title, type, value, category});// Option2, process it as an object
          })
          .on('error', error => {
            reject(error);
            throw new Error('Fail to process CSV file');
          })
          .on('end', () => {
            resolve();// ends the promise when CSV Parse send 'end' flag
          });
      });
    }


    await parseCSVPromise(); // now using the created promise - await finishing parsingCSV
    console.log('option1', results);// option1
    console.log('option2',newTransactions);// option2
    return response.json({ resultsCounter, results }); // For testing only - interrupting the rote execution
    

    // continue processing results and send it to dataBase...
    //await fs.promises.unlink(filePath); // optionally you can delete the file parsed/processed
    

вариант 1 ответ:

 [
  [ 'Loan', 'income', '1500', 'Others' ],
  [ 'Website Hosting', 'outcome', '50', 'Others' ],
  [ 'Ice cream', 'outcome', '3', 'Food' ]
 ]
  

Вариант 2 ответа:

  [
    { title: 'Loan',            type: 'income',  value: '1500', category: 'Others' },
    { title: 'Website Hosting', type: 'outcome', value:   '50', category: 'Others' },
    { title: 'Ice cream',       type: 'outcome', value:    '3', category: 'Food' }
  ]
person Ramon Menezes    schedule 06.10.2020