最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

multipartform data - MulterError: Unexpected field when uploading image file using Multer and Express.js - Stack Overflow

programmeradmin1浏览0评论

I am having trouble registering and the problem occurs when uploading an image file using Multer in my Express.js application in the form.
I am getting an error "MulterError: Unexpected field" when trying to upload an image file from the frontend. The problem occurs only the first time, and when I submit the same image again, the registering is successful.

the error:

[server] Multer Error: MulterError: Unexpected field
[server]     at wrappedFileFilter (D:\supergear-mongo\admin\node_modules\.pnpm\[email protected]\node_modules\multer\index.js:40:19)
[server]     at Multipart.<anonymous> (D:\supergear-mongo\admin\node_modules\.pnpm\[email protected]\node_modules\multer\lib\make-middleware.js:107:7)
[server]     at Multipart.emit (node:events:518:28)
[server]     at HeaderParser.cb (D:\supergear-mongo\admin\node_modules\.pnpm\[email protected]\node_modules\busboy\lib\types\multipart.js:358:14)  
[server]     at HeaderParser.push (D:\supergear-mongo\admin\node_modules\.pnpm\[email protected]\node_modules\busboy\lib\types\multipart.js:162:20)
[server]     at SBMH.ssCb [as _cb] (D:\supergear-mongo\admin\node_modules\.pnpm\[email protected]\node_modules\busboy\lib\types\multipart.js:394:37)
[server]     at feed (D:\supergear-mongo\admin\node_modules\.pnpm\[email protected]\node_modules\streamsearch\lib\sbmh.js:248:10)
[server]     at SBMH.push (D:\supergear-mongo\admin\node_modules\.pnpm\[email protected]\node_modules\streamsearch\lib\sbmh.js:104:16)       
[server]     at Multipart._write (D:\supergear-mongo\admin\node_modules\.pnpm\[email protected]\node_modules\busboy\lib\types\multipart.js:567:19) 
[server]     at writeOrBuffer (node:internal/streams/writable:572:12) {
[server]   code: 'LIMIT_UNEXPECTED_FILE',
[server]   field: 'avatar',
[server]   storageErrors: []
[server] }

my files:
store.ts:

/* eslint-disable @typescript-eslint/no-explicit-any */
import { create } from "zustand";
import { persist } from "zustand/middleware";
// import axios from "axios";
import { ProductProps } from "../../type";
import { config } from "../config";
import { getDataWithToken, loginpostData, regpostData } from ".";

interface APIResponse {
  token?: string;
  errors?: { msg: string }[]; //user.js - 23
}

interface CartProduct extends ProductProps {
  quantity: number;
}

interface UserType {
  firstName: string;
  lastName: string;
  password: string;
  email: string;
  avatar: string;
  id: string;
}

interface StoreType {
  // user
  currentUser: UserType | null;
  isLoading: boolean;
  // isAuthonticated: null;
  // token: localStorage.getItem("token");
  // registerUser: (userData: FormData) => Promise<void>;
  registerUser: (userData: FormData) => Promise<APIResponse>; // ✅ تحديث التوقيع ليعيد APIResponse
  loginUser: (userData: FormData) => Promise<APIResponse>; // ✅ تحديث التوقيع ليعيد APIResponse
  logoutUser: () => void;

  // getUserInfo: (userId: string) => Promise<void>;
  getUserInfo: () => Promise<void>;

  // cart
  cartProduct: CartProduct[];
  addToCart: (product: ProductProps) => void;
  decreaseQuantity: (productId: string) => void;
  removeFromCart: (productId: string) => void;
  resetCart: () => void;
  // favorite
  favoriteProduct: CartProduct[];
  addToFavorite: (product: ProductProps) => void;
  removeFromFavourite: (productId: string) => void;
  resetFavorite: () => void;
}

const customStorage = {
  getItem: (name: string) => {
    const item = localStorage.getItem(name);
    return item ? JSON.parse(item) : null;
  },
  setItem: (name: string, value: any) => {
    localStorage.setItem(name, JSON.stringify(value));
  },
  removeItem: (name: string) => {
    localStorage.removeItem(name);
  },
};

export const Store = create<StoreType>()(
  persist(
    (set) => ({
      currentUser: null,
      isLoading: true,
      cartProduct: [],
      favoriteProduct: [],

      registerUser: async (userData: FormData): Promise<APIResponse> => {
        try {
          set({ isLoading: true });
          const data = await regpostData(
            `${config.baseUrl}/api/users/register`,
            userData
          );
          set({ currentUser: data, isLoading: false });
          return data; // ✅ إرجاع البيانات بعد التسجيل
        } catch (error: any) {
          console.error("Error registering user", error);
          set({ currentUser: null, isLoading: false });
          console.error("error.response?.data", error.response?.data);
          return error.response?.data;
        }
      },

      loginUser: async (userData: FormData): Promise<APIResponse> => {
        try {
          set({ isLoading: true });
          const data = await loginpostData(
            `${config.baseUrl}/api/users/login`,
            userData
          );

          if (data.token) {
            localStorage.setItem("token", data.token);
      
            set({ currentUser: data, isLoading: false });
          }

          // set({ currentUser: data, isLoading: false });

          console.log(data);
          return data; // ✅ إرجاع البيانات بعد التسجيل

        } catch (error: any) {
          set({ currentUser: null, isLoading: false });
          console.error("error.response?.data", error.response?.data);
          return error.response?.data;
        }
      },

      logoutUser: () => {
        set({ currentUser: null }); // مسح معلومات المستخدم
        localStorage.removeItem("token"); // حذف الرمز المميز من التخزين المحلي
      },

      getUserInfo: async () => {
        try {
          set({ isLoading: true });

          const token = localStorage.getItem("token"); // الحصول على التوكن من localStorage
          if (!token) {
            throw new Error("No token found"); // إذا لم يتم العثور على التوكن
          }

          const data = await getDataWithToken(`${config.baseUrl}/api/users`, token); // استخدام الدالة الجديدة مع التوكن

          set({ currentUser: data, isLoading: false });
        } catch (error) {
          console.error("Error fetching user info", error);
          set({ currentUser: null, isLoading: false });
        }
      },

      addToCart: (product) => {
        set((state) => {
          const existingProduct = state.cartProduct.find(
            (p) => p._id === product._id
          );
          return {
            cartProduct: existingProduct
              ? state.cartProduct.map((p) =>
                  p._id === product._id ? { ...p, quantity: p.quantity + 1 } : p
                )
              : [...state.cartProduct, { ...product, quantity: 1 }],
          };
        });
      },

      decreaseQuantity: (productId) => {
        set((state) => ({
          // cartProduct: state.cartProduct.map((p) => p._id === productId ? { ...p, quantity: Math.max(p.quantity - 1, 1) } : p),
          cartProduct: state.cartProduct.map((p) =>
            String(p._id) === String(productId)
              ? { ...p, quantity: Math.max(p.quantity - 1, 1) }
              : p
          ),
        }));
      },

      removeFromCart: (productId) => {
        set((state) => ({
          // cartProduct: state.cartProduct.filter((item) => item._id !== productId),
          cartProduct: state.cartProduct.filter(
            (item) => String(item._id) !== String(productId)
          ),
        }));
      },

      resetCart: () => {
        set({ cartProduct: [] });
      },

      addToFavorite: (product) => {
        set((state) => {
          const isFavorite = state.favoriteProduct.some(
            (item) => item._id === product._id
          );
          return {
            favoriteProduct: isFavorite
              ? state.favoriteProduct.filter((item) => item._id !== product._id)
              : [...state.favoriteProduct, { ...product }],
          };
        });
      },

      removeFromFavourite: (productId) => {
        set((state) => ({
          // favoriteProduct: state.favoriteProduct.filter((item) => item._id !== productId),
          favoriteProduct: state.favoriteProduct.filter(
            (item) => String(item._id) !== String(productId)
          ),
        }));
      },

      resetFavorite: () => {
        set({ favoriteProduct: [] });
      },
    }),
    {
      name: "supergear-storage",
      storage: customStorage,
    }
  )
);

user.js:

const express = require("express");
const router = express.Router();
const { check, validationResult } = require("express-validator");
const User = require("../models/User");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const config = require("config");
const {auth, upload} = require("../utils/");

router.post(
  "/register",
  (req, res, next) => {
    upload(req, res, (err) => {
      if (err) {
        console.error("Multer Error:", err);
        return res.status(500).json({ error: "Multer error", details: err.message }); // إضافة رسالة خطأ مفصلة
      }
      next();
    });
  },
  // upload, // قم بتطبيق multer كـ middleware
  check("name", "name is required").notEmpty(),
  check("Lname", "name is required").notEmpty(),
  check("email", "please include valid email").isEmail(),
  check(
    "password",
    "please choose a password with atleast 6 characters"
  ).isLength({ min: 6 }),
  async (req, res) => {
    console.log("req.file بعد Multer:", req.file); // تسجيل req.file بعد Multer
    console.log("req.files بعد Multer:", req.files); // تسجيل req.files بعد Multer

    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      console.log("Validation Errors:", errors.array());

      return res.status(400).json({ errors: errors.array() });
    }
    
    const { name, Lname, email, password } = req.body;
    console.log(name, Lname, email, password );
    try {
      let user = await User.findOne({ email });
      if (user) {
        return res
          .status(400)
          .json({ errors: [{ msg: "User already exists" }] });
      }

      console.log("req.body:", req.body); // تسجيل req.body

      const avatar = req.file ? `/images/${req.file.filename}` : ""; // رابط الصورة
      console.log( "avatar = req.file ?",avatar );

      user = new User({ name, Lname, email, password, avatar  });
      const salt = await bcrypt.genSalt(10);
      user.password = await bcrypt.hash(password, salt);
      await user.save();

      const payload = {
        user: {
          id: user.id,
        },
      };

      jwt.sign(
        payload,
        config.get("jwtSecret"),
        { expiresIn: "5 days" },
        (err, token) => {
          if (err) throw err;
          else {
            res.json({ token });
          }
        }
      );
    } catch (err) {
      console.error(err.message);
      res.status(500).send(err.message);
    }
    // res.send("success in sending data");
  }
);

router.post(
    "/login",
    check("email", "please include valid email").isEmail(),
    check(
      "password",
      "please choose a password with atleast 6 characters"
    ).isLength({ min: 6 }),
    async (req, res) => {
      const errors = validationResult(req);
      if (!errors.isEmpty()) {
        return res.status(400).json({ errors: errors.array() });
      }

      const { email, password } = req.body;

      try {
        let user = await User.findOne({ email });
        if (!user) {
          return res
            .status(400)
            .json({ errors: [{ msg: "invalid condentials - email not found" }] });
        }
        const isMatch = await bcryptpare(password, user.password);
        if (!isMatch) {
          return res
           .status(400)
           .json({ errors: [{ msg: "invalid condentials - wrong password" }] });
        }

        const payload = {
          user: {
            id: user.id,
          },
        };
  
        jwt.sign(
          payload,
          config.get("jwtSecret"),
          { expiresIn: "5 days" },
          (err, token) => {
            if (err) throw err;
            else {
              res.json({ token });
            }
          }
        );
      } catch (err) {
        console.error(err.message);
        res.status(500).send(err.message);
      }
      // res.send("success in sending data");
    }
  );


  /* takes a token and returns user info private */
  router.get("/",auth, async(req, res) => {
    try {
        const user = await User.findById(req.user.id).select("-password");
        res.json(user);
    } catch (err) {
        console.error(err.message);
        res.status(500).send(err.message);
    }
})

module.exports = router;

regestration.tsx:

import { useState } from "react";
import { MdPhotoLibrary } from "react-icons/md";
import Login from "./Login";
import Label from "./Label";
// import upload from "../lib/upload";
import { Store } from "../lib/store";

const Registeration = () => {
  const [login, setLogin] = useState(true);
  const [loading, setLoading] = useState(false);
  const [errMsg, setErrMsg] = useState("");
  const [Avatar, setAvatar] = useState({
    file: null,
    url: "",
  });

  const handleAvatar = (e: any) => {
    if (e.target.files[0]) {
      setAvatar({
        file: e.target.files[0],
        url: URL.createObjectURL(e.target.files[0]),
      });
    }
  };

const handleRegistration = async (e: any) => {
  e.preventDefault();
  setErrMsg(""); // تصفير أي خطأ سابق

  const formData = new FormData(e.target);
  
  console.log("FormData entries:", Array.from(formData.entries())); // تسجيل formData entries

  if (Avatar.file) {
    formData.append("avatar", Avatar.file);
    console.log( "avatar", Avatar.file );

  }

  try {
    setLoading(true);
    const response = await Store.getState().registerUser(formData);
    
    if (response.errors && response.errors.length > 0) {
      setErrMsg(response.errors[0].msg); // عرض الخطأ في الواجهة
      return; // منع الانتقال إلى صفحة تسجيل الدخول
    }

    setLogin(true);
    
  } catch (error: any) {
    console.error("Error:", error);
    setErrMsg(error.message || "Something went wrong");
  } finally {
    setLoading(false);
  }
};

  return (
    <div>
      {login ? (
        <Login setLogin={setLogin} />
      ) : (
        <div className="bg-gray-950 rounded-lg">
          <form
            onSubmit={handleRegistration}
            className="max-w-5xl mx-auto pt-10 px-10 lg:px-0 text-white"
          >
            <div className="border-b border-b-white/10 pb-5">
              <h2 className="text-lg font-semibold uppercase leading-7">
                Registration Form
              </h2>
              <p className="mt-1 text-sm leading-6 text-gray-400">
                You need to provide required information to get register with
                us.
              </p>
            </div>
            <div className="border-b border-b-white/10 pb-5">
              <div className="mt-5 grid grid-cols-1 gap-x-6 gap-y-5 sm:grid-cols-6">
                <div className="sm:col-span-3">
                  <Label title="name" htmlFor="name" />
                  <input
                    type="text"
                    name="name"
                    className="block w-full rounded-md border-0 bg-white/5 py-1.5 px-4 outline-none text-white shadow-sm ring-1 ring-inset ring-white/10 focus:ring-skyText sm:text-sm sm:leading-6 mt-2"
                  />
                </div>
                <div className="sm:col-span-3">
                  <Label title="Last name" htmlFor="lastName" />
                  <input
                    type="text"
                    name="Lname"
                    className="block w-full rounded-md border-0 bg-white/5 py-1.5 px-4 outline-none text-white shadow-sm ring-1 ring-inset ring-white/10 focus:ring-skyText sm:text-sm sm:leading-6 mt-2"
                  />
                </div>
                <div className="sm:col-span-4">
                  <Label title="Email address" htmlFor="email" />
                  <input
                    type="email"
                    name="email"
                    className="block w-full rounded-md border-0 bg-white/5 py-1.5 px-4 outline-none text-white shadow-sm ring-1 ring-inset ring-white/10 focus:ring-skyText sm:text-sm sm:leading-6 mt-2"
                  />
                </div>
                <div className="sm:col-span-4">
                  <Label title="Password" htmlFor="password" />
                  <input
                    type="password"
                    name="password"
                    className="block w-full rounded-md border-0 bg-white/5 py-1.5 px-4 outline-none text-white shadow-sm ring-1 ring-inset ring-white/10 focus:ring-skyText sm:text-sm sm:leading-6 mt-2"
                  />
                </div>
                <div className="col-span-full">
                  <div className="mt-2 flex items-center gap-x-3">
                    <div className="flex-1">
                      <Label title="Cover photo" />
                      <div className="mt-2 flex justify-center rounded-lg border border-dashed border-white/25 px-6 py-4">
                        <div className="flex flex-col items-center text-center">
                          <div className="w-14 h-14 border border-gray-600 rounded-full p-1">
                            {Avatar?.url ? (
                              <img
                                src={Avatar?.url}
                                alt="userImage"
                                className="w-full h-full rounded-full object-cover"
                              />
                            ) : (
                              <MdPhotoLibrary className="mx-auto h-full w-full text-gray-500" />
                            )}
                          </div>
                          <div className="mt-4 flex items-center mb-1 text-sm leading-6 text-gray-400">
                            <label htmlFor="file-upload">
                              <span className="relative cursor-pointer rounded-md px-2 py-1 bg-gray-900 font-semibold text-gray-200 hover:bg-gray-800">
                                Upload a file
                              </span>
                              <input
                                type="file"
                                name="avatar"
                                id="file-upload"
                                className="sr-only"
                                onChange={handleAvatar}
                              />
                            </label>
                            <p className="pl-1">or drag and drop</p>
                          </div>
                          <p className="text-xs leading-5 text-gray-400">
                            PNG, JPG, GIF up to 10MB
                          </p>
                        </div>
                      </div>
                    </div>
                  </div>
                </div>
              </div>
            </div>
            {errMsg && (
              <p className="bg-white/90 text-red-600 text-center py-1 rounded-md tracking-wide font-semibold">
                {errMsg}
              </p>
            )}
            <button
              disabled={loading}
              type="submit"
              className={`mt-5 w-full py-2 uppercase text-base font-bold tracking-wide text-gray-300 rounded-md hover:text-white hover:bg-indigo-600 duration-200 ${
                loading ? "bg-gray-500 hover:bg-gray-500" : "bg-indigo-700"
              }`}
            >
              {loading ? "Loading..." : "Send"}
            </button>
          </form>

          <p className="text-sm leading-6 text-gray-400 text-center -mt-2 py-10">
            Already have an Account{" "}
            <button
              onClick={() => setLogin(true)}
              className="text-gray-200 font-semibold underline underline-offset-2 decoration-[1px] hover:text-white duration-200"
            >
              Login
            </button>
          </p>
        </div>
      )}
    </div>
  );
};

export default Registeration;

index.ts:

// الفيتش برتجع بيانات ال ءا بي ءاي الجسون ك استرنج
import axios from "axios";

export const getData = async (endpoint: string) => {
  try {
    const response = await fetch(endpoint, {
      method: "GET",
      headers: { "Content-Type": "application/json" },
    });
    if (!response.ok) {
      throw new Error("data fetch failed" + response.statusText);
    }
    const data = await response.json();
    return data;
  } catch (error) {
    console.log("error while fetching data", error);
    throw error;
  }
};

// export const postData = async (endpoint: string, formData: FormData) => {
//   try {
//     // const userData: {[key: string]: any} = {}; // تعريف نوع userData بشكل صحيح
//     // formData.forEach((value, key) => {
//     //   userData[key] = value;
//     // });
//     const userData = Object.fromEntries(formData.entries()); // تحويل FormData إلى كائن JavaScript مباشرة

//     const response = await fetch(endpoint, {
//       method: "POST",
//       // body: formData, // لازم تبقى بدون `JSON.stringify()`
//       headers: { "Content-Type": "application/json" },
//       body: JSON.stringify(userData), // تحويل الكائن إلى JSON
//     });

//     if (!response.ok) {
//       throw new Error("Failed to upload data: " + response.statusText);
//     }
//     const data = await response.json();
//     return data;
//   } catch (error) {
//     console.log("Error while uploading data", error);
//     throw error;
//   }
// };

export const regpostData = async (endpoint: string, formData: FormData) => {
  try {
    // const userData = Object.fromEntries(formData.entries()); // تحويل FormData إلى كائن JavaScript مباشرة

    // const response = await axios.post(endpoint, userData, {
    //   headers: { 'Content-Type': 'application/json' },
    // });

    const response = await axios.post(endpoint, formData);

    return response.data; // axios يرجع response.data مباشرة
  } catch (error) {
    console.error("Error while uploading data", error);
    throw error;
  }
};

export const loginpostData = async (endpoint: string, formData: FormData) => {
  try {
    const response = await axios.post(endpoint, formData, {
      headers: { "Content-Type": "application/json" },
    });

    return response.data; // axios يرجع response.data مباشرة
  } catch (error) {
    console.error("Error while uploading data", error);
    throw error;
  }
};

export const getDataWithToken = async (endpoint: string, token: string) => {
  try {
    const response = await axios.get(endpoint, {
      headers: { "x-auth-token": token }, // إرسال التوكن في رأس الطلب
    });
    return response.data;
  } catch (error) {
    console.error("Error while fetching data with token", error);
    throw error;
  }
};
const config = require("config");
const jwt = require("jsonwebtoken");
const multer = require("multer");

// بيمنع أي حد مش مسجل دخول من الوصول للـ روتس المحمية
const auth = (req, res, next) => {
  // get the token from the req header
  const token = req.header("x-auth-token");
  if (!token) {
    return res.status(401).json({ msg: "No token, authorization denied" });
  }

  try {
    jwt.verify(token, config.get("jwtSecret"), (err, decoded) => {
      if (err) {
        return res.status(401).json({ msg: "Token is not valid" });
      }
      req.user = decoded.user;
      next();
    });
  } catch (err) {
    console.error(err.message);
    res.status(500).json({ msg: err.message });
  }
};

const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, "public/images");
  },
  filename: (req, file, cb) => {
    cb(null, Date.now() + "-" + file.originalname);
    // cb(null, `${req.user.id}`);
  },
});

const upload = multer({storage: storage}).single("avatar")

module.exports = { auth, upload };

This code does not print anything

console.log("req.file after Multer:", req.file); // Log req.file after Multer

console.log("req.files after Multer:", req.files); // Log req.files after Multer

console.log("req.body:", req.body); // Log req.body

These codes are not written, even what is inside "" is not printed

+

The first time the register is executed, the image that I put in the form is not uploaded to the folder public/images

I expected field name mismatch to be the most common cause of the error.

I expected Multer's error handling to help determine the root cause of the error.

I expected req.file and req.files logging to help determine whether Multer was receiving the file.

I am having trouble registering and the problem occurs when uploading an image file using Multer in my Express.js application in the form.
I am getting an error "MulterError: Unexpected field" when trying to upload an image file from the frontend. The problem occurs only the first time, and when I submit the same image again, the registering is successful.

the error:

[server] Multer Error: MulterError: Unexpected field
[server]     at wrappedFileFilter (D:\supergear-mongo\admin\node_modules\.pnpm\[email protected]\node_modules\multer\index.js:40:19)
[server]     at Multipart.<anonymous> (D:\supergear-mongo\admin\node_modules\.pnpm\[email protected]\node_modules\multer\lib\make-middleware.js:107:7)
[server]     at Multipart.emit (node:events:518:28)
[server]     at HeaderParser.cb (D:\supergear-mongo\admin\node_modules\.pnpm\[email protected]\node_modules\busboy\lib\types\multipart.js:358:14)  
[server]     at HeaderParser.push (D:\supergear-mongo\admin\node_modules\.pnpm\[email protected]\node_modules\busboy\lib\types\multipart.js:162:20)
[server]     at SBMH.ssCb [as _cb] (D:\supergear-mongo\admin\node_modules\.pnpm\[email protected]\node_modules\busboy\lib\types\multipart.js:394:37)
[server]     at feed (D:\supergear-mongo\admin\node_modules\.pnpm\[email protected]\node_modules\streamsearch\lib\sbmh.js:248:10)
[server]     at SBMH.push (D:\supergear-mongo\admin\node_modules\.pnpm\[email protected]\node_modules\streamsearch\lib\sbmh.js:104:16)       
[server]     at Multipart._write (D:\supergear-mongo\admin\node_modules\.pnpm\[email protected]\node_modules\busboy\lib\types\multipart.js:567:19) 
[server]     at writeOrBuffer (node:internal/streams/writable:572:12) {
[server]   code: 'LIMIT_UNEXPECTED_FILE',
[server]   field: 'avatar',
[server]   storageErrors: []
[server] }

my files:
store.ts:

/* eslint-disable @typescript-eslint/no-explicit-any */
import { create } from "zustand";
import { persist } from "zustand/middleware";
// import axios from "axios";
import { ProductProps } from "../../type";
import { config } from "../config";
import { getDataWithToken, loginpostData, regpostData } from ".";

interface APIResponse {
  token?: string;
  errors?: { msg: string }[]; //user.js - 23
}

interface CartProduct extends ProductProps {
  quantity: number;
}

interface UserType {
  firstName: string;
  lastName: string;
  password: string;
  email: string;
  avatar: string;
  id: string;
}

interface StoreType {
  // user
  currentUser: UserType | null;
  isLoading: boolean;
  // isAuthonticated: null;
  // token: localStorage.getItem("token");
  // registerUser: (userData: FormData) => Promise<void>;
  registerUser: (userData: FormData) => Promise<APIResponse>; // ✅ تحديث التوقيع ليعيد APIResponse
  loginUser: (userData: FormData) => Promise<APIResponse>; // ✅ تحديث التوقيع ليعيد APIResponse
  logoutUser: () => void;

  // getUserInfo: (userId: string) => Promise<void>;
  getUserInfo: () => Promise<void>;

  // cart
  cartProduct: CartProduct[];
  addToCart: (product: ProductProps) => void;
  decreaseQuantity: (productId: string) => void;
  removeFromCart: (productId: string) => void;
  resetCart: () => void;
  // favorite
  favoriteProduct: CartProduct[];
  addToFavorite: (product: ProductProps) => void;
  removeFromFavourite: (productId: string) => void;
  resetFavorite: () => void;
}

const customStorage = {
  getItem: (name: string) => {
    const item = localStorage.getItem(name);
    return item ? JSON.parse(item) : null;
  },
  setItem: (name: string, value: any) => {
    localStorage.setItem(name, JSON.stringify(value));
  },
  removeItem: (name: string) => {
    localStorage.removeItem(name);
  },
};

export const Store = create<StoreType>()(
  persist(
    (set) => ({
      currentUser: null,
      isLoading: true,
      cartProduct: [],
      favoriteProduct: [],

      registerUser: async (userData: FormData): Promise<APIResponse> => {
        try {
          set({ isLoading: true });
          const data = await regpostData(
            `${config.baseUrl}/api/users/register`,
            userData
          );
          set({ currentUser: data, isLoading: false });
          return data; // ✅ إرجاع البيانات بعد التسجيل
        } catch (error: any) {
          console.error("Error registering user", error);
          set({ currentUser: null, isLoading: false });
          console.error("error.response?.data", error.response?.data);
          return error.response?.data;
        }
      },

      loginUser: async (userData: FormData): Promise<APIResponse> => {
        try {
          set({ isLoading: true });
          const data = await loginpostData(
            `${config.baseUrl}/api/users/login`,
            userData
          );

          if (data.token) {
            localStorage.setItem("token", data.token);
      
            set({ currentUser: data, isLoading: false });
          }

          // set({ currentUser: data, isLoading: false });

          console.log(data);
          return data; // ✅ إرجاع البيانات بعد التسجيل

        } catch (error: any) {
          set({ currentUser: null, isLoading: false });
          console.error("error.response?.data", error.response?.data);
          return error.response?.data;
        }
      },

      logoutUser: () => {
        set({ currentUser: null }); // مسح معلومات المستخدم
        localStorage.removeItem("token"); // حذف الرمز المميز من التخزين المحلي
      },

      getUserInfo: async () => {
        try {
          set({ isLoading: true });

          const token = localStorage.getItem("token"); // الحصول على التوكن من localStorage
          if (!token) {
            throw new Error("No token found"); // إذا لم يتم العثور على التوكن
          }

          const data = await getDataWithToken(`${config.baseUrl}/api/users`, token); // استخدام الدالة الجديدة مع التوكن

          set({ currentUser: data, isLoading: false });
        } catch (error) {
          console.error("Error fetching user info", error);
          set({ currentUser: null, isLoading: false });
        }
      },

      addToCart: (product) => {
        set((state) => {
          const existingProduct = state.cartProduct.find(
            (p) => p._id === product._id
          );
          return {
            cartProduct: existingProduct
              ? state.cartProduct.map((p) =>
                  p._id === product._id ? { ...p, quantity: p.quantity + 1 } : p
                )
              : [...state.cartProduct, { ...product, quantity: 1 }],
          };
        });
      },

      decreaseQuantity: (productId) => {
        set((state) => ({
          // cartProduct: state.cartProduct.map((p) => p._id === productId ? { ...p, quantity: Math.max(p.quantity - 1, 1) } : p),
          cartProduct: state.cartProduct.map((p) =>
            String(p._id) === String(productId)
              ? { ...p, quantity: Math.max(p.quantity - 1, 1) }
              : p
          ),
        }));
      },

      removeFromCart: (productId) => {
        set((state) => ({
          // cartProduct: state.cartProduct.filter((item) => item._id !== productId),
          cartProduct: state.cartProduct.filter(
            (item) => String(item._id) !== String(productId)
          ),
        }));
      },

      resetCart: () => {
        set({ cartProduct: [] });
      },

      addToFavorite: (product) => {
        set((state) => {
          const isFavorite = state.favoriteProduct.some(
            (item) => item._id === product._id
          );
          return {
            favoriteProduct: isFavorite
              ? state.favoriteProduct.filter((item) => item._id !== product._id)
              : [...state.favoriteProduct, { ...product }],
          };
        });
      },

      removeFromFavourite: (productId) => {
        set((state) => ({
          // favoriteProduct: state.favoriteProduct.filter((item) => item._id !== productId),
          favoriteProduct: state.favoriteProduct.filter(
            (item) => String(item._id) !== String(productId)
          ),
        }));
      },

      resetFavorite: () => {
        set({ favoriteProduct: [] });
      },
    }),
    {
      name: "supergear-storage",
      storage: customStorage,
    }
  )
);

user.js:

const express = require("express");
const router = express.Router();
const { check, validationResult } = require("express-validator");
const User = require("../models/User");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const config = require("config");
const {auth, upload} = require("../utils/");

router.post(
  "/register",
  (req, res, next) => {
    upload(req, res, (err) => {
      if (err) {
        console.error("Multer Error:", err);
        return res.status(500).json({ error: "Multer error", details: err.message }); // إضافة رسالة خطأ مفصلة
      }
      next();
    });
  },
  // upload, // قم بتطبيق multer كـ middleware
  check("name", "name is required").notEmpty(),
  check("Lname", "name is required").notEmpty(),
  check("email", "please include valid email").isEmail(),
  check(
    "password",
    "please choose a password with atleast 6 characters"
  ).isLength({ min: 6 }),
  async (req, res) => {
    console.log("req.file بعد Multer:", req.file); // تسجيل req.file بعد Multer
    console.log("req.files بعد Multer:", req.files); // تسجيل req.files بعد Multer

    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      console.log("Validation Errors:", errors.array());

      return res.status(400).json({ errors: errors.array() });
    }
    
    const { name, Lname, email, password } = req.body;
    console.log(name, Lname, email, password );
    try {
      let user = await User.findOne({ email });
      if (user) {
        return res
          .status(400)
          .json({ errors: [{ msg: "User already exists" }] });
      }

      console.log("req.body:", req.body); // تسجيل req.body

      const avatar = req.file ? `/images/${req.file.filename}` : ""; // رابط الصورة
      console.log( "avatar = req.file ?",avatar );

      user = new User({ name, Lname, email, password, avatar  });
      const salt = await bcrypt.genSalt(10);
      user.password = await bcrypt.hash(password, salt);
      await user.save();

      const payload = {
        user: {
          id: user.id,
        },
      };

      jwt.sign(
        payload,
        config.get("jwtSecret"),
        { expiresIn: "5 days" },
        (err, token) => {
          if (err) throw err;
          else {
            res.json({ token });
          }
        }
      );
    } catch (err) {
      console.error(err.message);
      res.status(500).send(err.message);
    }
    // res.send("success in sending data");
  }
);

router.post(
    "/login",
    check("email", "please include valid email").isEmail(),
    check(
      "password",
      "please choose a password with atleast 6 characters"
    ).isLength({ min: 6 }),
    async (req, res) => {
      const errors = validationResult(req);
      if (!errors.isEmpty()) {
        return res.status(400).json({ errors: errors.array() });
      }

      const { email, password } = req.body;

      try {
        let user = await User.findOne({ email });
        if (!user) {
          return res
            .status(400)
            .json({ errors: [{ msg: "invalid condentials - email not found" }] });
        }
        const isMatch = await bcryptpare(password, user.password);
        if (!isMatch) {
          return res
           .status(400)
           .json({ errors: [{ msg: "invalid condentials - wrong password" }] });
        }

        const payload = {
          user: {
            id: user.id,
          },
        };
  
        jwt.sign(
          payload,
          config.get("jwtSecret"),
          { expiresIn: "5 days" },
          (err, token) => {
            if (err) throw err;
            else {
              res.json({ token });
            }
          }
        );
      } catch (err) {
        console.error(err.message);
        res.status(500).send(err.message);
      }
      // res.send("success in sending data");
    }
  );


  /* takes a token and returns user info private */
  router.get("/",auth, async(req, res) => {
    try {
        const user = await User.findById(req.user.id).select("-password");
        res.json(user);
    } catch (err) {
        console.error(err.message);
        res.status(500).send(err.message);
    }
})

module.exports = router;

regestration.tsx:

import { useState } from "react";
import { MdPhotoLibrary } from "react-icons/md";
import Login from "./Login";
import Label from "./Label";
// import upload from "../lib/upload";
import { Store } from "../lib/store";

const Registeration = () => {
  const [login, setLogin] = useState(true);
  const [loading, setLoading] = useState(false);
  const [errMsg, setErrMsg] = useState("");
  const [Avatar, setAvatar] = useState({
    file: null,
    url: "",
  });

  const handleAvatar = (e: any) => {
    if (e.target.files[0]) {
      setAvatar({
        file: e.target.files[0],
        url: URL.createObjectURL(e.target.files[0]),
      });
    }
  };

const handleRegistration = async (e: any) => {
  e.preventDefault();
  setErrMsg(""); // تصفير أي خطأ سابق

  const formData = new FormData(e.target);
  
  console.log("FormData entries:", Array.from(formData.entries())); // تسجيل formData entries

  if (Avatar.file) {
    formData.append("avatar", Avatar.file);
    console.log( "avatar", Avatar.file );

  }

  try {
    setLoading(true);
    const response = await Store.getState().registerUser(formData);
    
    if (response.errors && response.errors.length > 0) {
      setErrMsg(response.errors[0].msg); // عرض الخطأ في الواجهة
      return; // منع الانتقال إلى صفحة تسجيل الدخول
    }

    setLogin(true);
    
  } catch (error: any) {
    console.error("Error:", error);
    setErrMsg(error.message || "Something went wrong");
  } finally {
    setLoading(false);
  }
};

  return (
    <div>
      {login ? (
        <Login setLogin={setLogin} />
      ) : (
        <div className="bg-gray-950 rounded-lg">
          <form
            onSubmit={handleRegistration}
            className="max-w-5xl mx-auto pt-10 px-10 lg:px-0 text-white"
          >
            <div className="border-b border-b-white/10 pb-5">
              <h2 className="text-lg font-semibold uppercase leading-7">
                Registration Form
              </h2>
              <p className="mt-1 text-sm leading-6 text-gray-400">
                You need to provide required information to get register with
                us.
              </p>
            </div>
            <div className="border-b border-b-white/10 pb-5">
              <div className="mt-5 grid grid-cols-1 gap-x-6 gap-y-5 sm:grid-cols-6">
                <div className="sm:col-span-3">
                  <Label title="name" htmlFor="name" />
                  <input
                    type="text"
                    name="name"
                    className="block w-full rounded-md border-0 bg-white/5 py-1.5 px-4 outline-none text-white shadow-sm ring-1 ring-inset ring-white/10 focus:ring-skyText sm:text-sm sm:leading-6 mt-2"
                  />
                </div>
                <div className="sm:col-span-3">
                  <Label title="Last name" htmlFor="lastName" />
                  <input
                    type="text"
                    name="Lname"
                    className="block w-full rounded-md border-0 bg-white/5 py-1.5 px-4 outline-none text-white shadow-sm ring-1 ring-inset ring-white/10 focus:ring-skyText sm:text-sm sm:leading-6 mt-2"
                  />
                </div>
                <div className="sm:col-span-4">
                  <Label title="Email address" htmlFor="email" />
                  <input
                    type="email"
                    name="email"
                    className="block w-full rounded-md border-0 bg-white/5 py-1.5 px-4 outline-none text-white shadow-sm ring-1 ring-inset ring-white/10 focus:ring-skyText sm:text-sm sm:leading-6 mt-2"
                  />
                </div>
                <div className="sm:col-span-4">
                  <Label title="Password" htmlFor="password" />
                  <input
                    type="password"
                    name="password"
                    className="block w-full rounded-md border-0 bg-white/5 py-1.5 px-4 outline-none text-white shadow-sm ring-1 ring-inset ring-white/10 focus:ring-skyText sm:text-sm sm:leading-6 mt-2"
                  />
                </div>
                <div className="col-span-full">
                  <div className="mt-2 flex items-center gap-x-3">
                    <div className="flex-1">
                      <Label title="Cover photo" />
                      <div className="mt-2 flex justify-center rounded-lg border border-dashed border-white/25 px-6 py-4">
                        <div className="flex flex-col items-center text-center">
                          <div className="w-14 h-14 border border-gray-600 rounded-full p-1">
                            {Avatar?.url ? (
                              <img
                                src={Avatar?.url}
                                alt="userImage"
                                className="w-full h-full rounded-full object-cover"
                              />
                            ) : (
                              <MdPhotoLibrary className="mx-auto h-full w-full text-gray-500" />
                            )}
                          </div>
                          <div className="mt-4 flex items-center mb-1 text-sm leading-6 text-gray-400">
                            <label htmlFor="file-upload">
                              <span className="relative cursor-pointer rounded-md px-2 py-1 bg-gray-900 font-semibold text-gray-200 hover:bg-gray-800">
                                Upload a file
                              </span>
                              <input
                                type="file"
                                name="avatar"
                                id="file-upload"
                                className="sr-only"
                                onChange={handleAvatar}
                              />
                            </label>
                            <p className="pl-1">or drag and drop</p>
                          </div>
                          <p className="text-xs leading-5 text-gray-400">
                            PNG, JPG, GIF up to 10MB
                          </p>
                        </div>
                      </div>
                    </div>
                  </div>
                </div>
              </div>
            </div>
            {errMsg && (
              <p className="bg-white/90 text-red-600 text-center py-1 rounded-md tracking-wide font-semibold">
                {errMsg}
              </p>
            )}
            <button
              disabled={loading}
              type="submit"
              className={`mt-5 w-full py-2 uppercase text-base font-bold tracking-wide text-gray-300 rounded-md hover:text-white hover:bg-indigo-600 duration-200 ${
                loading ? "bg-gray-500 hover:bg-gray-500" : "bg-indigo-700"
              }`}
            >
              {loading ? "Loading..." : "Send"}
            </button>
          </form>

          <p className="text-sm leading-6 text-gray-400 text-center -mt-2 py-10">
            Already have an Account{" "}
            <button
              onClick={() => setLogin(true)}
              className="text-gray-200 font-semibold underline underline-offset-2 decoration-[1px] hover:text-white duration-200"
            >
              Login
            </button>
          </p>
        </div>
      )}
    </div>
  );
};

export default Registeration;

index.ts:

// الفيتش برتجع بيانات ال ءا بي ءاي الجسون ك استرنج
import axios from "axios";

export const getData = async (endpoint: string) => {
  try {
    const response = await fetch(endpoint, {
      method: "GET",
      headers: { "Content-Type": "application/json" },
    });
    if (!response.ok) {
      throw new Error("data fetch failed" + response.statusText);
    }
    const data = await response.json();
    return data;
  } catch (error) {
    console.log("error while fetching data", error);
    throw error;
  }
};

// export const postData = async (endpoint: string, formData: FormData) => {
//   try {
//     // const userData: {[key: string]: any} = {}; // تعريف نوع userData بشكل صحيح
//     // formData.forEach((value, key) => {
//     //   userData[key] = value;
//     // });
//     const userData = Object.fromEntries(formData.entries()); // تحويل FormData إلى كائن JavaScript مباشرة

//     const response = await fetch(endpoint, {
//       method: "POST",
//       // body: formData, // لازم تبقى بدون `JSON.stringify()`
//       headers: { "Content-Type": "application/json" },
//       body: JSON.stringify(userData), // تحويل الكائن إلى JSON
//     });

//     if (!response.ok) {
//       throw new Error("Failed to upload data: " + response.statusText);
//     }
//     const data = await response.json();
//     return data;
//   } catch (error) {
//     console.log("Error while uploading data", error);
//     throw error;
//   }
// };

export const regpostData = async (endpoint: string, formData: FormData) => {
  try {
    // const userData = Object.fromEntries(formData.entries()); // تحويل FormData إلى كائن JavaScript مباشرة

    // const response = await axios.post(endpoint, userData, {
    //   headers: { 'Content-Type': 'application/json' },
    // });

    const response = await axios.post(endpoint, formData);

    return response.data; // axios يرجع response.data مباشرة
  } catch (error) {
    console.error("Error while uploading data", error);
    throw error;
  }
};

export const loginpostData = async (endpoint: string, formData: FormData) => {
  try {
    const response = await axios.post(endpoint, formData, {
      headers: { "Content-Type": "application/json" },
    });

    return response.data; // axios يرجع response.data مباشرة
  } catch (error) {
    console.error("Error while uploading data", error);
    throw error;
  }
};

export const getDataWithToken = async (endpoint: string, token: string) => {
  try {
    const response = await axios.get(endpoint, {
      headers: { "x-auth-token": token }, // إرسال التوكن في رأس الطلب
    });
    return response.data;
  } catch (error) {
    console.error("Error while fetching data with token", error);
    throw error;
  }
};
const config = require("config");
const jwt = require("jsonwebtoken");
const multer = require("multer");

// بيمنع أي حد مش مسجل دخول من الوصول للـ روتس المحمية
const auth = (req, res, next) => {
  // get the token from the req header
  const token = req.header("x-auth-token");
  if (!token) {
    return res.status(401).json({ msg: "No token, authorization denied" });
  }

  try {
    jwt.verify(token, config.get("jwtSecret"), (err, decoded) => {
      if (err) {
        return res.status(401).json({ msg: "Token is not valid" });
      }
      req.user = decoded.user;
      next();
    });
  } catch (err) {
    console.error(err.message);
    res.status(500).json({ msg: err.message });
  }
};

const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, "public/images");
  },
  filename: (req, file, cb) => {
    cb(null, Date.now() + "-" + file.originalname);
    // cb(null, `${req.user.id}`);
  },
});

const upload = multer({storage: storage}).single("avatar")

module.exports = { auth, upload };

This code does not print anything

console.log("req.file after Multer:", req.file); // Log req.file after Multer

console.log("req.files after Multer:", req.files); // Log req.files after Multer

console.log("req.body:", req.body); // Log req.body

These codes are not written, even what is inside "" is not printed

+

The first time the register is executed, the image that I put in the form is not uploaded to the folder public/images

I expected field name mismatch to be the most common cause of the error.

I expected Multer's error handling to help determine the root cause of the error.

I expected req.file and req.files logging to help determine whether Multer was receiving the file.

Share Improve this question edited Mar 8 at 8:41 eslam wael asked Mar 8 at 7:54 eslam waeleslam wael 371 silver badge5 bronze badges 2
  • You're posting a lot of code, but where is your Multer config/setup? – robertklep Commented Mar 8 at 8:01
  • i post it @robertklep – eslam wael Commented Mar 8 at 8:42
Add a comment  | 

1 Answer 1

Reset to default 1

My guess would be that you're adding two files to formData.

Here:

const formData = new FormData(e.target);

And here:

if (Avatar.file) {
  formData.append("avatar", Avatar.file);
  console.log( "avatar", Avatar.file );
}

Since Multer is set up to only expect a single file, this will cause an error.

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论