how to access fields and files in form.parse of formidable library

177 Views Asked by At

I wrote a file upload handler like this to get a user avatar and also the user info like username and etc to signup a new user

const fs = require('fs');
const path = require('path');
const formidable = require('formidable');
const { getFieldsAndFiles } = require('../controllers/userAuth');

const checkFileType = (file) => {
  const type = file[0].mimetype.split('/').pop();
  const validTypes = ['png', 'jpeg', 'jpg'];
  if (validTypes.indexOf(type) == -1) {
    console.log('The file type is invalid');
    return false;
  }
  return true;
}

const uploadAvatar = async (req, res, next) => {
  // console.log(req.body);
  const form = new formidable.IncomingForm();
  const uploadsFolder = path.join(__dirname, '../uploads/avatars');
  form.multiples = true;
  form.maxFileSize = 50 * 1024 * 1024; //50 MB
  form.uploadDir = uploadsFolder;
  form.parse(req, async (error, fields, files) => {

    getFieldsAndFiles(fields, files);

    if (error) {
      console.log(error, 'Error parsing files');
      return res.json({ok: false, msg: 'Error parsing files'})
    };

    if (files.avatar) {
      const file = files.avatar
      const isValid = checkFileType(file);
      const fileName = encodeURIComponent(file[0].originalFilename.replace(/&. *;+/g, '-'))
      console.log('file name is: ', fileName);
      if (!isValid) {
        return res.json({ok: false, msg: 'File type is invalid'})
      }
      try {
        console.log('file path is: ', file[0].filepath );
        fs.rename(file[0].filepath, path.join(uploadsFolder, fileName), (error) => {
          if (error) {
            console.log('file upload failed:', error);
            // Handle the error and return an appropriate response
            return res.json({ ok: false, msg: 'Upload failed' });
          }
          // File renamed successfully, continue with the rest of the code
        });

      } catch (error) {
        console.log(error, 'file upload failed, trying to remove the temp file...');
        try { fs.unlink(file[0].path) } catch (error) { };
        return res.json({ ok: false, msg: 'Upload failed' });
      }
    } else if(files.files instanceof Array && files.files.length > 0) {
    } else {
      return res.json({ ok: false, msg: 'No files uploaded' });
    }
    // return res.status(200).json({ ok: true, msg: 'File uploaded successfuly' })
  })
  next()
}

module.exports = uploadAvatar ;

and I need to extract and export the fields and files value from form.parse callback function to use it inside my signup function in controller file. I made a function named getFieldsAndFiles inside the controller file and called it in this formidable file to pass the fields and files to it. this is the controller file

const UserModel = require('../models/UserModel');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
const dotenv = require('dotenv');

dotenv.config();
const secretKey = process.env.SEC_KEY;
const createToken = function (_id) {
  return jwt.sign({ _id }, secretKey, { expiresIn: '1d' })
};

let username;
let email;
let password;
let firstName;
let lastName;
let birthDate;
let phoneNumber;
let avatar;
let avatarPath;

const getFieldsAndFiles = async (fields, files) => {
  username = fields.username[0].trimLeft();
  email = fields.email[0].trimLeft();
  password = fields.password[0].trimLeft();
  firstName = fields.firstName[0].trimLeft();
  lastName = fields.lastName[0].trimLeft();
  birthDate = fields.birthDate[0].trimLeft();
  phoneNumber = fields.phoneNumber[0].trimLeft();
  avatar = files.avatar[0]
  avatarPath = avatar.filepath
  console.log('username in getFieldsAndFiles:', username);
  console.log('avatar path getFieldsAndFiles:', avatarPath);
};

const signupUser = async (req, res) => {
  console.log('begining of controller');
  console.log('username in signup function:', username);
  
  try {
    const usernameExists = await UserModel.findOne({ username });
    if (usernameExists) return (res.status(400).json({ error: 'Username is already in use.' }));

    const emailExists = await UserModel.findOne({ email });
    if (emailExists) return (res.status(400).json({ error: 'Email is already in use.' }));

    if (password.length < 8) return (res.status(400).json({ error: 'Password is not strong enough.' }));

    const salt = await bcrypt.genSalt(10);
    const hashedPassword = await bcrypt.hash(password, salt);

    const newUser = await UserModel.create({
      username,
      firstName,
      lastName,
      email,
      password: hashedPassword,
      friends,
      birthDate,
      avatar: avatarPath,
      phoneNumber,
      city,
      status,
      occupation,
      education,
      location,
      bio,
      gender
    })
    const token = createToken(newUser._id);
    console.log(newUser);
    res.status(201).json({ newUser, token });

  } catch (error) {
    res.status(500).json({ error: error.message })
  }
};

const signinUser = async (req, res) => {
  try {
    const { username, password } = req.body;
    const user = await UserModel.findOne({ username });
    console.log(user);
    const token = createToken(user._id);
    if (!user) return (res.status(400).json({ error: "User does not exist." }));
    const isMatch = await bcrypt.compare(password, user.password);
    if (!isMatch) return (res.status(400).json({ error: "Invalid credentials." }));
    res.status(200).json({ user, token });

  } catch (error) {
    res.status(400).json({ error: error.message });
  };
};

module.exports = {
  signupUser,
  signinUser,
  getFieldsAndFiles
}

but the scope and all those things won't let me use those extracted values for user info inside my signup function to save the new user signedup.

I'm fully open for any change in my code so I can have access to fields and files which are containing the formData texts of user info and uploaded file.

P.S: There is no req.body or req.file because of formData

0

There are 0 best solutions below