Nodejs无法返回Busboy Finish Event的数据

发布于 2025-02-11 05:40:04 字数 2979 浏览 1 评论 0原文

我目前正在尝试开发Google云功能以解析多部分文件(Excel格式或CSV),以填充Firestore数据库。

我正在使用助手功能中的Busboy来解析文件,将其转换为JSON并将其返回到主函数。

一切顺利,直到我试图返回解析的数据。我认为最逻辑的方法是将数据从Busboy“ finish”事件中返回,但似乎并没有将数据返回,因为它是在主函数中返回的,因此未定义。我首先想到了与异步代码执行有关的某些问题,但是当我尝试在Busboy Finish事件中打印数据时,它可以正常工作。

我试图在网上找到一些相关的内容,但不幸的是没有成功。这是我的助手功能:我

// Takes a multipart request and sends back redable data
const processRequest = (req) => {
    const busboy = Busboy({headers: req.headers});

    formats = ['application/vnd.ms-excel', 'text/csv', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'];

    var finalData;
        
    // fieldname is the request key name of the file
    // file is the stream 
    // fname is the name of the fileq
    busboy.on('file', (fieldname, file, fname) => {
        
        // Checks if file is right format
        if(!formats.includes(fname.mimeType)) throw new FileFormatError('File must be excel or csv');

        bytes = [];

        // Checks that the request key is the right one
        if(fieldname == 'file') {

            // Data is the actual bytes, adds it to the buffer each time received
            file.on('data', (data) => {
                bytes.push(data);
            });
            
            // Concatenates the bytes into a buffer and reads data given mimetype
            file.on('end', async () => {
                buffer = Buffer.concat(bytes);
                
                if(fname.mimeType === 'application/vnd.ms-excel' ||
                    fname.mimeType === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') {
                    workbook = XLSX.read(buffer, {type: 'buffer'});
                    json = excelToJson(workbook);
                    console.log(json);
                    finalData = json;
                }

                if (fname.mimeType === 'text/csv') {
                    var csv = [];
                    const stream  = Readable.from(buffer.toString());
                    stream.pipe(CSV.parse({delimiter: ','}))
                    .on('error', (err) => {
                        console.log('csv parsing error');
                        console.log(err.message);
                    }).on('data', (row) => {
                        csv.push(row);
                    }).on('end', () => {
                        console.log('csv file properly processed');
                        console.log(csv);
                        // CSV PARSING LOGIC TO COME, JUST TESTING RIGHT NOW
                        finalData = csv;
                    });
                    
                } 
            });

        }
    });

    busboy.on('finish', () => {
        console.log('busboy finished');
        return finalData;

        // WHEN ONLY PRINTED THE DATA IS PRESENT AND DISPLAYS PROPERLY HERE
    })

    // Processes request body bytes
    busboy.end(req.rawBody);
}

一定有一些我误会的东西,但到目前为止,我无法指出什么。

预先感谢您的时间:)

I am currently trying to develop a google cloud function to parse multipart files (excel format or csv) in order to populate the firestore database.

I am using busboy in a helper function to parse the file, convert it to json and return it to the main function.

Everything goes well until I am trying to return the parsed data. I thought the most logic way of doing was to return the data from the busboy 'finish' event but it seems not to return the data as once back in the main function it is undefined. I first thought of some issue related to asynchronous code execution but when I tried to only print the data in the busboy finish event it worked properly.

I've tried to find some related content online but unfortunately didnt success. Here is my helper function :

// Takes a multipart request and sends back redable data
const processRequest = (req) => {
    const busboy = Busboy({headers: req.headers});

    formats = ['application/vnd.ms-excel', 'text/csv', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'];

    var finalData;
        
    // fieldname is the request key name of the file
    // file is the stream 
    // fname is the name of the fileq
    busboy.on('file', (fieldname, file, fname) => {
        
        // Checks if file is right format
        if(!formats.includes(fname.mimeType)) throw new FileFormatError('File must be excel or csv');

        bytes = [];

        // Checks that the request key is the right one
        if(fieldname == 'file') {

            // Data is the actual bytes, adds it to the buffer each time received
            file.on('data', (data) => {
                bytes.push(data);
            });
            
            // Concatenates the bytes into a buffer and reads data given mimetype
            file.on('end', async () => {
                buffer = Buffer.concat(bytes);
                
                if(fname.mimeType === 'application/vnd.ms-excel' ||
                    fname.mimeType === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') {
                    workbook = XLSX.read(buffer, {type: 'buffer'});
                    json = excelToJson(workbook);
                    console.log(json);
                    finalData = json;
                }

                if (fname.mimeType === 'text/csv') {
                    var csv = [];
                    const stream  = Readable.from(buffer.toString());
                    stream.pipe(CSV.parse({delimiter: ','}))
                    .on('error', (err) => {
                        console.log('csv parsing error');
                        console.log(err.message);
                    }).on('data', (row) => {
                        csv.push(row);
                    }).on('end', () => {
                        console.log('csv file properly processed');
                        console.log(csv);
                        // CSV PARSING LOGIC TO COME, JUST TESTING RIGHT NOW
                        finalData = csv;
                    });
                    
                } 
            });

        }
    });

    busboy.on('finish', () => {
        console.log('busboy finished');
        return finalData;

        // WHEN ONLY PRINTED THE DATA IS PRESENT AND DISPLAYS PROPERLY HERE
    })

    // Processes request body bytes
    busboy.end(req.rawBody);
}

There must be something I am misunderstanding but as of yet I cannot point out what.

Thanks in advance for your time :)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

老子叫无熙 2025-02-18 05:40:04

您不会等待CSV解析实际完成。

最好将异步代码重构以使用async/等待

由于您正在使用可能只支持回调式异步的库,因此您需要做一些新的Promise包装自己。

可以理解的是,我尚未测试以下代码,但是类似的内容...

/**
 * Parse the given buffer as a CSV, return a promise of rows
 */
function parseCSV(buffer) {
  return new Promise((resolve, reject) => {
    const csv = [];
    const stream = Readable.from(buffer.toString());
    stream
      .pipe("text/csv".parse({ delimiter: "," }))
      .on("error", reject)
      .on("data", (row) => csv.push(row))
      .on("end", () => resolve(csv));
  });
}

/**
 * Parse the given buffer as a spreadsheet, return a promise
 */
async function parseSpreadsheet(mimeType, buffer) {
  if (
    mimeType === "application/vnd.ms-excel" ||
    mimeType === "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
  ) {
    const workbook = XLSX.read(buffer, { type: "buffer" });
    return excelToJson(workbook);
  }
  if (mimeType === "text/csv") {
    return parseCSV(buffer);
  }
  throw new Error(`Unknown mime type ${mimeType}`);
}

/**
 * Get the bytes of the field `fieldName` in the request.
 */
function getFileFromRequest(req, fieldName) {
  return new Promise((resolve, reject) => {
    const busboy = Busboy({ headers: req.headers });
    busboy.on("file", (name, file, info) => {
      // Only process the field we care about
      if (name != fieldName) {
        return;
      }
      const bytes = [];
      file.on("data", (data) => bytes.push(data));
      file.on("end", () =>
        resolve({
          info,
          buffer: Buffer.concat(bytes),
        }),
      );
      file.on("error", reject);
    });
    busboy.end(req.rawBody);
  });
}

async function parseRequest(req) {
  // (1) Get the file as a buffer
  const { info, buffer } = await getFileFromRequest(req, "file");
  // (2) Try parsing it as a spreadsheet
  const data = await parseSpreadsheet(info.mimeType, buffer);
  // (3) Do something with the data?
  return data;
}

You're not waiting for your CSV parsing to actually finish.

It would be better to refactor your async code to use async/await.

Since you're using libraries that might only support callback-style async, you'll need to do some new Promise wrapping yourself.

Understandably, I haven't tested the below code, but something like this...

/**
 * Parse the given buffer as a CSV, return a promise of rows
 */
function parseCSV(buffer) {
  return new Promise((resolve, reject) => {
    const csv = [];
    const stream = Readable.from(buffer.toString());
    stream
      .pipe("text/csv".parse({ delimiter: "," }))
      .on("error", reject)
      .on("data", (row) => csv.push(row))
      .on("end", () => resolve(csv));
  });
}

/**
 * Parse the given buffer as a spreadsheet, return a promise
 */
async function parseSpreadsheet(mimeType, buffer) {
  if (
    mimeType === "application/vnd.ms-excel" ||
    mimeType === "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
  ) {
    const workbook = XLSX.read(buffer, { type: "buffer" });
    return excelToJson(workbook);
  }
  if (mimeType === "text/csv") {
    return parseCSV(buffer);
  }
  throw new Error(`Unknown mime type ${mimeType}`);
}

/**
 * Get the bytes of the field `fieldName` in the request.
 */
function getFileFromRequest(req, fieldName) {
  return new Promise((resolve, reject) => {
    const busboy = Busboy({ headers: req.headers });
    busboy.on("file", (name, file, info) => {
      // Only process the field we care about
      if (name != fieldName) {
        return;
      }
      const bytes = [];
      file.on("data", (data) => bytes.push(data));
      file.on("end", () =>
        resolve({
          info,
          buffer: Buffer.concat(bytes),
        }),
      );
      file.on("error", reject);
    });
    busboy.end(req.rawBody);
  });
}

async function parseRequest(req) {
  // (1) Get the file as a buffer
  const { info, buffer } = await getFileFromRequest(req, "file");
  // (2) Try parsing it as a spreadsheet
  const data = await parseSpreadsheet(info.mimeType, buffer);
  // (3) Do something with the data?
  return data;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文