Express 서버에서 CSV 업로드, 행 구문 분석 및 각 행을 MongoDB(Mongoose 사용)에 저장

4495 단어 mongodbcsvnode
시행착오를 거듭한 결과입니다. 나는 스트림과 그렇지 않은 것에 대한 단서가 없었기 때문에 왜 그렇게 오래 걸렸습니까? D

다음 라이브러리가 필요합니다.
  • multer
  • @fast-csv/parse
  • streamifier

  • CSV 파일을 업로드하기 위한 양식 만들기 - multipart/form-data



    파일 업로드는 multipart/form-data을 통해 이루어져야 합니다. 이것은 저도 최근에 알게 된 내용이며 아마도 다른 게시물의 대상이 될 것입니다. 지금은 건너뛰겠습니다.

    멀터


    multer 은 파일을 가져와 req.file 에 배치합니다. req.body에서 파일을 찾을 것이라고 기대하지 마십시오. 이 파일에는 텍스트인 양식 필드 데이터만 포함됩니다. multer에 대한 자습서의 90%는 수신 파일을 디렉토리에 저장하는 방법을 설명합니다. 이것은 쓰기 권한이 없는 서버에 상주하므로 파일이 메모리에 상주하기를 원합니다.

    const multer = require("multer");
    
    const parseCsv = multer().single("whatever-name-you-gave-to-the-input-field-in-your-form");
    
    module.exports = {parseCsv}
    


    이것은 req.file에 파일을 배치할 미들웨어입니다.

    빠른 csv 및 streamifier


    req.file 에는 buffer 속성이 있지만 노드의 createReadStream 에서는 읽을 수 없습니다. fs.createReadStream(buffer)을 시도하면 이것이 파일이 아니라는 오류가 발생할 가능성이 높습니다. Node의 createReadStreamBuffer의 인스턴스를 허용하지만(우리의 buffer은 인스턴스임) 해당 인스턴스는 createReadStream에서 읽을 수 없습니다. 이 SO answer에서 그것에 대해 배웠습니다. 내가 찾은 해결책은? streamifier , 처음 알게 된 here . 소스 코드를 보면 req.file 의 버퍼를 createReadStream 으로 전달되는 읽을 수 있는 버퍼로 변환하는 마법을 부립니다. 이 도서관을 발견하게 되어 기뻤습니다.

    그래서 이렇게 스트림을 만듭니다.

    const { buffer } = req.file;
    
    streamifier.createReadStream(buffer)
    


    @빠른 csv/구문 분석


    @fast-csv/parse은 csv에서 데이터가 포함된 스트림을 가져오고 몇 가지 이벤트를 호출하여 파일 내용을 구문 분석합니다. 모든 행에 대해 .on('data', data => callback)을 호출하므로 원하는 대로 무엇이든 할 수 있습니다. 모든 행이 구문 분석되면 .on('end', rowCount => callback) 을 호출합니다. 이벤트 .on('error', callback)validation capabilities과 관련이 있다고 생각하지만 아직 시도하지 않았습니다.

    fast-csv를 csv으로 가져온 다음 .pipe(csv.parse())을 호출할 수 있습니다(아래 예 참조). 또한 옵션을 csv.parse() 에 전달할 수 있습니다. 지금까지 사용한 옵션은 headers: true (csv 파일에서 헤더 줄 건너뛰기, 문서 here 참조) 및 ignoreEmpty: true (빈 줄 무시, 문서 here 참조)입니다.

    내 첫 번째 반복은 모든 행 구문 분석에서 문서 생성을 배치하는 것이 었습니다. DB에 데이터를 저장하는 비동기 특성과 CSV 파싱의 동기화 특성으로 인한 실수입니다. 첫 번째 문서가 저장되기 전에 'end' 이벤트가 트리거되어 내 전략과 서버 응답이 망가지는 것을 발견했습니다.

    나는 약간의 연구를했고 잘 작동하는 strategy을 찾았습니다. 구문 분석 된 행 (객체로 다시 반환됨)을 메모리의 배열에 추가하고 Model.create([ARRAY_OF_OBJECTS]) 이벤트에서 Mongoose의 'end'을 호출합니다. 이를 비동기화하고 클라이언트에 대한 서버 응답을 결정해야 합니다. 마찬가지로 저에게는 잘 작동하는 것 같습니다.

    const csv = require("@fast-csv/parse");
    const streamifier = require("streamifier");
    
    // somewhere below
    
    router.post("/endpoint", [multerMiddlewareExplainedAbove], (req, res) => {
      const { buffer } = req.file;
    
      const dataFromRows = [];
    
      streamifier
        .createReadStream(buffer)
        .pipe(csv.parse({ headers: true, ignoreEmpty: true })) // <== this is @fast-csv/parse!!
        .on("data", (row) => {
          dataFromRows .push(row);
        })
        .on("end", async (rowCount) => {
          try {
            const data = await MyModelName.create(dataFromRows );
            res.status(200).json({ rowCount, data });
          } catch (error) {
            res.status(400).json({ error});
          }
        });
    });
    


    그것이 의미가 있기를 바랍니다. 나는 물건을 발견하는대로 물건을 추가 할 것입니다. 읽어 주셔서 감사합니다 (:

    좋은 웹페이지 즐겨찾기