0

I am trying to create upload profile image method that help user upload their profile picture on website but I am having trouble with I dont know how to send the image from client to server and make those image store on cloudinary or firebase.

My routes look like this: ProfileAPI.js

    const express = require("express");
    const router = express.Router();
    const { body, param } = require("express-validator");
    const { catchErrors } = require("../errors/errorHandlers");
    const multer = require('multer');
    const uuidv4 = require('uuid/v4');
    
    const upload_dir = './images';
    
    const storage = multer.diskStorage({
      destination: (req, file, cb) => {
        cb(null, upload_dir);
      },
      filename: (req, file, cb) => {
        cb(null, `${uuidv4()}-${file.filename.toLowerCase}`);
      }
    });
    
    const upload = multer({
      storage: storage,
      fileFilter: (req, file, cb) => {
        if (
          file.mimetype == 'image/png' ||
          file.mimetype == 'image/jpg' ||
          file.mimetype == 'image/jpeg'
        ) {
          cb(null, true);
        } else {
          cb(null, false);
          return cb(new Error('Only .png, .jpg and .jpeg format allowed!'));
        }
      }
    });
const {
  getUserProfile,
  getUsersPublicProfile,
  lookUpId,
  updateUserProfile,
  updateUserEmail,
  deleteUserProfile,
  // deleteUserSkill,
  addPlayersProfile,
  getCreatedPlayers,
  updatePlayersProfile,
  deleteUserCreatedPlayer,
} = require("./profilesController");
    
    router.post(
      "/upload",
      upload.single('profileImg'),
      updateUserProfile
    );

So key points are the setup of storage which tells where to upload + the file filter in upload, right?

And the route.post which will `upload.single('profileImg'), right? the route will include my controller for updateUserProfile which can be found here: profilesController.js

exports.updateUserProfile = async (req, res) => {
  const userId = req.session.passport.user.id;
  // This array will contain all the update functions to run.
  const updates = [];

  // If a gravatar url has not been generated, do it now.
  const pictureValue = gravatar.url(
    req.body.email,
    { s: "100", r: "pg", d: "retro" },
    true
  );

  const payload = {
    fullname: req.body.fullname,
    location: req.body.location,
    webpage: req.body.webpage,
    linkedin: req.body.linkedin,
    institution: req.body.institution,
    bio: req.body.bio,
    major: req.body.major,
    mergedTo: userId,
    picture: pictureValue,
    skillone: req.body.skillone,
    skilltwo: req.body.skilltwo,
    skillthree: req.body.skillthree
  };
}

So now to the frontend code (react.js):

This is the form I am loading in my react app:

UserProfile.js

const UserProfile = (serverUserData) => {
  const appState = useContext(GlobalContext);
  const { currentUser } = appState;
  const { email, picture, name } = currentUser;
  const [isVerified, setIsVerified] = useState(false);

  const checkVerificationData = () => {
    axios.get("/api/v1/profiles/profile").then((res) => {
      const { data } = res;   
      if (data.verifiedDT) {
        setIsVerified(data.verifiedDT);
      }
    });
  };

  useEffect(() => {
    checkVerificationData();
  }, [isVerified]);


    // Upload user avatar function
  const [imageSelected, setImageSelected] = useState("");

  const handleSubmit = (e) => {
    e.preventDefault();
    const formData = new FormData();
    formData.append('email', email);
    formData.append('name', name);
    formData.append('profileImg', imageSelected);

    axios
      .post(`/upload`, formData)
      .then(() => console.log("success"))
      .catch(err => console.log(err));
  };

  const onFileChange = (e) => {
    setImageSelected({ profileImg: e.target.files[0] });
  };
    };

 const classes = useStyles();
  return (
    <div className={classes.root}>
      <Grid item xs={12}
        container
        direction="row"
        justify="center"
        alignItems="center"
        spacing={4}>
        <Grid item>
          <Grid item>
            <UserCard
              picture={currentUser.picture}
              userEmail={email}
              name={name}
              isVerified={isVerified}
              handleSubmit={handleSubmit}
              onFileChange={onFileChange}
            />
            <br />
          </Grid>
        

and here is where user can upload their profile photo: UserCard.js

  {picture ? (
    <div>
      <Avatar
        src={picture}
        alt="Avatar"
        className="avatar--profile_image"
      />  
      <input
        type="file"
        onChange={onFileChange}
      />  
      <button onClick={handleSubmit}>Submit</button>
    </div>
  ) : (
    <AccountCircleIcon className="avatar--profile_image" />
  )}

So when entering things and hitting the Add Button my api states that req.file is undefined and I cannot find out why.

Can anyone help me drilling down the error?

4
  • To upload files, you need to use contentType: "multipart/form-data" in axios post request first paramenter. Commented Mar 25, 2021 at 10:49
  • How do I write it? @AmilaSenadheera Commented Mar 25, 2021 at 10:51
  • Did you add body-parser to your express app. Other things seem okay to me Commented Mar 25, 2021 at 12:29
  • Yes I did. Would you mind to hop in a zoom call and I can share my screen? Commented Mar 25, 2021 at 12:30

2 Answers 2

1

To upload files you need to use contentType: "multipart/form-data". Use the following as a reference to achieve the file upload.

helper function to create a instance with requied header. You may add any others to here.

const getInstance = () => {
  return axios.create({
    headers: {
      "Content-Type": "multipart/form-data",
    },
  });
}

call this method with the file to be uploaded

const fileUplaod = (file) => {

    let formData = new FormData();
    formData.append("images", file, file.name);
    
    getInstance()
      .post(endpoint_post_url, formData)
      .then((response) => {
        console.log("IMAGES_SUBMIT_SUCCESS");
      })
      .catch((err) => {
        console.error("image submit error", err);
      });
}

check the request body in your backend code. You can upload multiple images as well to the same property. It will be an array in the request object.

Sign up to request clarification or add additional context in comments.

8 Comments

@NathanNguyen, could you get it done? Let me know any problem you face with that. I'm happy to help.
Do I need onFileChange or just delete it ?
keep it. Just use the imageSelected instead of file. add a button to your UI and assign fileUpload as the onClick handler
Can you take a look my profileAPI. Did I write my route correct?
Nope, change upload.single('profileImg') as upload.single('images'). Or change the name as you want in formData.
|
0

Edit the handleSubmit function to add a config to the axios call.

const handleSubmit = (e) => {
    e.preventDefault();
    const formData = new FormData();
    formData.append('email', email);
    formData.append('name', name);
    formData.append('profileImg', imageSelected);

    axios
      .post(`/upload`, formData, {
         headers: {
           'Content-Type': "multipart/form-data"
         }
      })
      .then(() => console.log("success"))
      .catch(err => console.log(err));
  };

1 Comment

Can you check my profileAPI code? did I write my routes right?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.