mscdex / busboy

A streaming parser for HTML form data for node.js
MIT License
2.84k stars 213 forks source link

Error: Unexpected end of form #357

Closed Koikarp8877 closed 6 months ago

Koikarp8877 commented 6 months ago

Hello, i'm trying to upload files on my nextjs 14.0.4 app with busboy, however, while the files upload in the right repo, with the right name and right extension, they are unsuable "Not a JPEG file: starts with 0xef 0xbf", exemple with an jpeg file.

This was my original issue, however, after this, and changing to the busboy v 1.6.0. I get "Error: Unexpected end of form". I tried all the version starting from 1.0.0.

The full console output :

API resolved without sending a response for /api/endpoints/ressources, this may result in stalled requests.
Erreur lors du traitement du formulaire : Error: Unexpected end of form
    at Multipart._final (/home/koi/Backend_Famdev/famdev/node_modules/busboy/lib/types/multipart.js:588:17)
    at prefinish (node:internal/streams/writable:906:14)
    at finishMaybe (node:internal/streams/writable:920:5)
    at Writable.end (node:internal/streams/writable:835:5)
    at onend (node:internal/streams/readable:946:10)
    at process.processTicksAndRejections (node:internal/process/task_queues:77:11)
Erreur busboy : Error: Unexpected end of form
    at Multipart._final (/home/koi/Backend_Famdev/famdev/node_modules/busboy/lib/types/multipart.js:588:17)
    at prefinish (node:internal/streams/writable:906:14)
    at finishMaybe (node:internal/streams/writable:920:5)
    at Writable.end (node:internal/streams/writable:835:5)
    at onend (node:internal/streams/readable:946:10)
    at process.processTicksAndRejections (node:internal/process/task_queues:77:11)
Done parsing form!
Ressource créée: {
  ressource_titre: null,
  ressource_date: 2024-03-15T00:00:00.000Z,
  ressource_contenu: null,
  id_commentaire: null,
  id_utilisateur: null,
  id_categorie: null,
  ressource_nombre_de_vues: 0,
  ressource_id: 1096,
  ressource_date_modification: null,
  ressource_type_modification: null,
  ressource_ajout: null,
  ressource_modification: null,
  ressource_visibilite: null,
  ressource_type_acces: null,
  status_id: null,
  ressource_media: null
}
Aucun fichier envoyé

And here is my full code

import { PrismaClient } from '@prisma/client';
const Busboy = require('busboy');
import fs from 'fs';
import path from 'path';

const prisma = new PrismaClient();

export const createRessource = async (req, res) => {
  try {
    const bb = Busboy({
      headers: req.headers,
      limits: {
        fileSize: 10 * 1024 * 1024 // 10 Mo
      }
    });

    const formData = {};

    bb.on('file', (fieldname, file, info) => {
      const { filename, encoding, mimeType } = info;
      console.log(`File [${fieldname}]: filename: ${filename}, encoding: ${encoding}, mimeType: ${mimeType}`);

      const buffers = [];

      file.on('data', (data) => {
        buffers.push(data);
        console.log(`File [${filename}] got ${data.length} bytes`);
      });

      file.on('end', () => {
        console.log(`File [${filename}] done`);
        const fileData = Buffer.concat(buffers);
        const uniquefileName = `${new Date().getTime()}.${path.extname(filename).slice(1)}`;
        const paths = `public/images/${uniquefileName}`;

        fs.writeFileSync(paths, fileData, 'binary');

        console.log(`File [${filename}] saved to [${paths}]`);
        formData[fieldname] = {
          filename: uniquefileName,
          path: paths,
          mimetype: mimeType,
          buffer: fileData,
        };
      });
    });

    bb.on('field', (fieldname, value, info) => {
      const { nameTruncated, valueTruncated, encoding, mimeType } = info;
      console.log(`Field [${fieldname}]: value: ${value}, nameTruncated: ${nameTruncated}, valueTruncated: ${valueTruncated}, encoding: ${encoding}, mimeType: ${mimeType}`);
      formData[fieldname] = value;
    });

    bb.on('close', async () => {
      console.log('Done parsing form!');
      const { titre, contenu, categorieName, visibilite, filefield } = formData;
      let fileInfo;
      if (filefield && formData[filefield]) {
        fileInfo = formData[filefield];
      }
      const currentDate = new Date();
      const newRessource = await prisma.t_ressource.create({
        data: {
          ressource_titre: titre,
          ressource_date: currentDate,
          ressource_contenu: contenu,
          id_categorie: categorieName,
          ressource_media: fileInfo || null,
          ressource_visibilite: visibilite,
        },
      });

      console.log('Ressource créée:', newRessource);
      if (fileInfo) {
        console.log('Nom du fichier :', fileInfo.filename);
      } else {
        console.log('Aucun fichier envoyé');
      }

      res.json({
        success: true,
        message: 'Ressource créée avec succès',
        ressource: newRessource,
      });
    });

    bb.on('error', (err) => {
      console.error('Erreur lors du traitement du formulaire :', err);
      res.status(500).json({
        success: false,
        message: 'Erreur lors du traitement du formulaire',
      });
    });

    bb.on('aborted', () => {
      console.error('Le téléchargement a été interrompu');
      res.status(500).json({
        success: false,
        message: 'Le téléchargement a été interrompu',
      });
    });

    bb.on('error', (err) => {
      console.error('Erreur busboy :', err);
      res.status(500).json({
        success: false,
        message: 'Une erreur est survenue lors du traitement du formulaire',
        error: err.message
      });
    });

    req.pipe(bb);
  } catch (error) {
    console.error('Erreur lors de la création de la ressource:', error);
    res.status(500).json({
      success: false,
      message: 'Erreur lors de la création de la ressource',
    });
  } finally {
    await prisma.$disconnect();
  }
};

I double checked the files are fine. It's my first nextjs app, any help would be greatly aprreciated. Thanks for your time.

Ps : sorry for the previous versions, i sent the wrong code.

mscdex commented 6 months ago

I can't help you with the nextjs side of things. If the client is converting binary data to UTF-8 and mangling the data in the process or anything like that, that's something you will have to solve on the nextjs end.

As far as the "unexpected end of form" goes, it's most likely going to be one of:

Koikarp8877 commented 6 months ago

I can't help you with the nextjs side of things. If the client is converting binary data to UTF-8 and mangling the data in the process or anything like that, that's something you will have to solve on the nextjs end.

As far as the "unexpected end of form" goes, it's most likely going to be one of:

  • The client is not sending the request in the proper format
  • Somewhere on the server side the request data stream is being severed
  • If you're using the node http server it may be timing out the request if the request is taking too long.

Thanks for answering !

So there is no apparent issue with how i handle the file upload in this code ?

I will try all you suggestions, thanks again for your time !

mscdex commented 6 months ago

So there is no apparent issue with how i handle the file upload in this code ?

Not really.

Koikarp8877 commented 6 months ago

What do you mean by this ? Is it possible to send multipart without form-data ?

Thanks for your time.

mscdex commented 6 months ago

Is it possible to send multipart without form-data ?

busboy only supports multipart/form-data and application/x-www-form-urlencoded because those are the traditional formats transmitted by browsers when submitting HTML forms. For any other format you will need to handle that yourself if you cannot reconfigure the client to use one of the two supported formats.

Koikarp8877 commented 6 months ago

Hello again,

I've solved the issue ; You were righr, the issie indeed lied in the way next js was handling the data.

Thanks again for your help !