How do I send multiple encoding types via a post method and nodejs?

Viewed 120

I am trying to send a name and description text data along with a video, but I can only send either the video or the text, not both.

This is the .ejs code for the post file:

<!DOCTYPE html>
<html>
<body>
    <form action="/uploadvideo" enctype="multipart/form-data" method="POST">
        <input type="text" name="name" placeholder="Name" required/>
        <input type="text" name="description" placeholder="Description" required/>
        <input type="file" name="video" accept=".mp4" required/>
        <input type="submit" value="Upload"/>
    </form>
</body>
</html>

This is the nodejs code for the post method:

var storage = multer.diskStorage({
    destination: function (req, file, cb) {
        const directories = getDirectories('views/videos/')
        const nextNumber = parseInt(directories[directories.length - 1])
        if (nextNumber === NaN)
        {
            cb(new Error('Cannot save file, NaN'), null)
        }
        else
        {
            cb(null, 'views/videos/' + nextNumber)
        }
    },
    filename: function (req, file, cb) {
        cb(null, 'video.mp4')
    }
})

var upload = multer({storage: storage})

app.post('/uploadvideo', (req, res, next) => {
    const directories = getDirectories('views/videos/')
    const nextNumber = parseInt(directories[directories.length - 1]) + 1
    if (!fs.existsSync('views/videos/' + nextNumber))
    {
        fs.mkdirSync('views/videos/' + nextNumber)
    }
    fs.writeFileSync('views/videos/' + nextNumber + '/data.txt', req.body.name + '\n' + req.body.description + '\n')
    fs.writeFileSync('views/videos/' + nextNumber + '/video.mp4', 'error')
    return next()
}, upload.any('video'), (req, res, next) => {
    const directories = getDirectories('views/videos/')
    const nextNumber = parseInt(directories[directories.length - 1])
    if (fs.readFileSync('views/videos/' + nextNumber + '/video.mp4').toString() === 'error')
    {
        rimraf.sync('views/videos/' + nextNumber)
        res.render('error.ejs', {message: "Error uploading video"})
    }
    else
    {
        loadVideo(nextNumber)
        res.redirect('/video?video=' + nextNumber)
    }
})

If I send set the enctype to "multipart/form-data" only the video is sent and the text is undefined, but if I use "application/x-www-form-urlencoded" only the text sends, but the video doesn't.

Is there a way I can send both? Thanks in advance.

1 Answers

Attach multer first, it will separate req.file from req.body:

const fsPromises = require('fs').promises;
const path = require('path');
const pathToVideos = path.join(__dirname, 'views/videos/');

app.post(
  '/uploadvideo',
  upload.any('video'),
  async (req, res, next) => {
    const directories = getDirectories(pathToVideos);
    const nextNumber = parseInt(directories[directories.length - 1]) + 1;
    const newVideoFolder = pathToVideos + '/' + nextNumber;

    try {
      // checking if folder exists
      const exists = await fsPromises.access(newVideoFolder).then(() => true).catch(() => false);
      if (!exists) await fsPromises.mkdir(newVideoFolder); // need to create

      // writing to file info to data.txt
      await fsPromises.writeFile(
        path.join(newVideoFolder, 'data.txt'),
        req.body.name + '\n' + req.body.description + '\n'
      );

      // if file not exists throw error
      await fsPromises.access(
        path.join(newVideoFolder, 'video.mp4')
      );

      loadVideo(nextNumber);
      res.redirect('/video?video=' + nextNumber);
    } catch (error) {
      rimraf(newVideoFolder, () => {});
      console.error(error);
      res.render('error.ejs', {message: "Error uploading video"});
    }
  });

P.S. Use promises with async/await (manual: https://nodejs.org/dist/latest-v10.x/docs/api/fs.html#fs_fs_promises_api)

examples: https://www.codota.com/code/javascript/functions/fs/promises

Related