user10596155
user10596155

Reputation: 81

upload csv file to @fastify/multipart

i am using nodejs , fastify , @fastify/multipart , fast-csv
to upload a csv file and get the data of it
so i made a plugin that takes files and get the data
but i tried to log the data its empty
using this code

  instance.decorate('uploadCSV', async (parts: Multipart[]) => {
const uploads = parts.map(async (part: Multipart) => {
  try {
    if (part.file) {
      const mimetype = part.mimetype;
      const fileName = part.filename.split(' ').join('');

      if (!fileName.toLowerCase().endsWith('.csv') || mimetype !== 'text/csv') {
        throw instance.httpErrors.badRequest('file.not.csv');
      }

      const uploadDir = path.join(__dirname, 'uploads');

      // Create the 'uploads' directory if it doesn't exist
      if (!fs.existsSync(uploadDir)) {
        fs.mkdirSync(uploadDir);
      }

      const filePath = path.join(uploadDir, fileName);

      // Save the file directly using fs.promises.writeFile
      await fs.promises.writeFile(filePath, part.file, 'utf-8');

      // Parse the CSV file
      const data = await new Promise<any[]>((resolve, reject) => {
        const dataArray: any[] = [];
        fs.createReadStream(filePath)
          .pipe(csv.parse({ headers: true }))
          .on('data', (row) => dataArray.push(row))
          .on('end', () => resolve(dataArray))
          .on('error', reject);
      });

      // Print the CSV data
      console.log('CSV Data:', data);

      // You can now do further processing with the parsed data

      return { success: true, message: 'CSV file uploaded and processed successfully' };
    }
  } catch (error) {
    throw error;
  }
});

// Wait for all uploads to complete
await Promise.all(uploads);
});

tried to use it by

const { file } = request.body
await instance.uploadCSV(file)

and it always getting me
CSV Data: [] empty array

how to fix that

Upvotes: 0

Views: 780

Answers (1)

Abhishek Chandel
Abhishek Chandel

Reputation: 199

I could not find what's exactly wrong with your code. However I have a working and better version of it.

my version of file upload will not stall event loop, because it is not doing any sync system calls.

You can take inspiration from following snippet:

import Fastify from 'fastify'
import multipart from '@fastify/multipart'
import fs from 'fs'
import util from 'util'
import { pipeline } from 'stream'
import csv from 'fast-csv';
import path from 'path'

const pump = util.promisify(pipeline)

export const app = Fastify({
  logger: true
})

app.register(multipart);

app.decorate('uploadCsv', async function (parts) {
  const folder = './uploads';
  await createFolderIfMissing(folder);
  for await (const part of parts) {
    if (part.file) {
      const destFilePath = path.join(folder, part.filename);
      // upload and save the file
      await pump(part.file, fs.createWriteStream(destFilePath));
      console.log(await readCsv(destFilePath));
    }
  }

  return { message: 'files uploaded' }
});

app.post('/upload', async function (req, reply) {
  const parts = req.parts()
  return app.uploadCsv(parts);
})

async function createFolderIfMissing(folderName) {
  try {
    await fs.promises.stat(folderName);
  } catch (error) {
    console.error("folder not found,.. creating..");
    await fs.promises.mkdir(folderName);
  }
}

function readCsv(filePath) {
  // Parse the CSV file
  return new Promise((resolve, reject) => {
    const dataArray = [];
    fs.createReadStream(filePath)
      .pipe(csv.parse({ headers: true, delimiter: ";" }))
      .on('data', (row) => dataArray.push(row))
      .on('end', () => resolve(dataArray))
      .on('error', reject);
  });
}

const start = async () => {
  try {
    await app.listen({ port: 3000 })
    console.log(`server listening on ${app.server.address().port}`)
  } catch (err) {
    app.log.error(err)
    process.exit(1)
  }
}

start()

Upvotes: 0

Related Questions