Search code examples
javascriptreactjsreduxaxiosmulter

Upload article with file in Mern-Stack not works


I want to upload an article with file. When I set into my request:

headers: {
            "Content-Type": "multipart/form-data",
            "token": `Bearer ${token}`
          },

then I get this Error:

Multipart: Boundary not found.

I have read, that I should not set the Content-Type in my axios-request, but when I do this, I get the 403 error out of my backend. I tried to append all fields in FormData, but I am getting the above error either. I have googled "Boundaries" in axios and tried it with "getHeaders()", but then the console says "getHeaders()" is not a function.

That is my frontend code:

const [formdata, setFormdata] = useState(
    {
      ressort:"",
      theme:"",
      title:"",
      content:"",
    }
  )
  const {ressort,theme,title,content} = formdata;
  const [fileData, setFileData] = useState({
    img:""
  })
  const {img} = fileData;
  const fileInput = useRef(img);
  const fileChange = (e)=>{
    console.log(fileInput.current.files);
    setFileData(fileInput.current.files[0])
  }
  console.log(fileData); //works
  const handleChange = (e)=>{
    setFormdata((prevState)=>({
      ...prevState,
      [e.target.name]: e.target.value
    }))
  }
  const onSubmit = (e)=>{
    e.preventDefault();
    const data = new FormData();
    data.append("img", fileData);
     data.append("ressort", formdata.ressort);
     data.append("theme", formdata.theme);
     data.append("title", formdata.title);
     data.append("content", formdata.content)
    console.log(data);
    const mainnewsData ={
      data,
    }
    console.log(mainnewsData);
    dispatch(createMainNews(mainnewsData));
  }

Here is my redux service:

  const API_URL = "http://localhost:5000/api/mainNews/";
const createMainNews = async (mainnewsData, token)=>{
    const config = {
        headers: {
            token: `Bearer ${token}`,
            'Content-Type': `multipart/form-data;`,
          },
        //   transformRequest: (data, error) => { //gives 403
        //     return mainnewsData;
        // }
    }
    const response = await axios.post(API_URL, mainnewsData, config);
    return response.data;
}

Here is my backend:

router.post("/", upload.single("img"),  verifyTokenAndAuthorization, async (req,res)=>{
    const newMainNews = new MainNews({
        img: req.file.originalname,
        ressort: req.body.ressort,
        theme: req.body.theme,
        title: req.body.title,
        content:req.body.content,
    });
    console.log(newMainNews);
    try{
        const savedMainNews = await newMainNews.save();
        res.status(200).json(savedMainNews);
    } catch(error){
        res.status(403)
        throw new Error("Action failed");
    }
});

That is my multer-storage:

const multer = require("multer");

 const storage = multer.diskStorage({
        destination:(req,file, callback)=>{
            callback(null, '../../frontside/public/uploads/')
        },
        fileName: (req, file, callback)=>{
            callback(null, Date.now()+ "--"+ file.originalname)
        }
    })

const upload = multer({storage:storage});

module.exports = upload;

Solution

  • I found out the solution by myself. Here is some code you can work with. The Frontend:

    const MainNews = () => {
      const { mainnews, isLoading, isError, message} = useSelector((state)=>state.mainnews);
      const dispatch = useDispatch();
      useEffect(()=>{
        if(isError){
          window.alert(message);
        }
          dispatch(getAllMainNews());
    
          return ()=>{
            dispatch(reset());
          }
        
      }, [dispatch, isError, message]);
      //text
      const [formdata, setFormdata] = useState(
        {
          ressort:"",
          theme:"",
          title:"",
          content:"",
        }
      )
      const {ressort,theme,title,content} = formdata;
      //file
      const [fileData, setFileData] = useState({
        img:""
      })
      const {img} = fileData;
      const fileInput = useRef(img);
      //change and preview
      const [preview, setPreview] = useState("");
    
      const fileChange = (e)=>{
        const file = fileInput.current.files[0];
          setFileData(file); 
        handlePreview(file)
      }
      const handlePreview = (file)=>{
        const reader = new FileReader();
        reader.readAsDataURL(file);
        reader.onloadend = ()=>{
          setPreview(reader.result);
        }
      } 
      console.log(fileData); //works
      //text
      const handleChange = (e)=>{
        setFormdata((prevState)=>({
          ...prevState,
          [e.target.name]: e.target.value
        }))
      }
      const onSubmit = (e)=>{
        e.preventDefault();
        const mainnewsData = new FormData();
        mainnewsData.append("ressort", formdata.ressort);
        mainnewsData.append("theme", formdata.theme);
        mainnewsData.append("title", formdata.title);
        mainnewsData.append("content", formdata.content);
        mainnewsData.append("img", fileData);
        for(let value of mainnewsData){
          console.log(value);
        }
       
        dispatch(createMainNews(mainnewsData));
      }
    

    http-request in my redux-service:

    const createMainNews = async (mainnewsData, token)=>{
        const config = {
            headers: {
                // 'Content-Type': `multipart/form-data`,
                'Content-Type' : 'application/json',
                 token: `Bearer ${token}`,
              },
        }
        const response = await axios.post(API_URL, mainnewsData, config);
        console.log(response);
        return response.data;
    }
    

    multer:

    const storage = multer.diskStorage({
            destination:(req,file, callback)=>{
                callback(null,  path.resolve(process.cwd(), 'frontside/public/uploads'));
            },
            fileName: (req, file, callback)=>{
                callback(null, Date.now()+ "--"+ path.extname(file.originalname));
            }
        })
    
    const upload = multer({storage:storage});
    
    module.exports = upload;
    

    Don't forget in your index.js:

    app.use(express.static(path.resolve(process.cwd(), 'frontside/public/')));
    

    And the backend mongoose Schema:

    const MainnewsSchema = new mongoose.Schema({
        img:{type:String, required:true},
        cloudinary_id:{type:String, required:true},
        ressort:{type:String, required:true},
        theme:{type:String, required:true},
        title:{type:String, required:true},
        content:{type:String, required:true},
        clicked:{type:Number, default:0},
    }, 
    

    My backend:

    router.post("/", upload.single("img"), verifyTokenAndAuthorization, async (req,res)=>{
            // const {img, cloudinary_id, ressort, theme, title, content} = req.body;
        try{
            const uploadResult = await cloudinary.uploader.upload(req.file.path,{
                upload_preset: "Mern_redux-practice",
                resource_type: "auto",
            });
            //     console.log("Upload successfull", JSON.stringify(uploadResult, null, 2));
          
            const newMainNews = new MainNews({
                cloudinary_id : uploadResult.public_id,
                ressort: req.body.ressort,
                theme: req.body.theme,
                title: req.body.title,
                content: req.body.content,
                img: uploadResult.secure_url,
            });
           console.log(newMainNews);
            const savedMainNews = await newMainNews.save();
            res.status(200).json(savedMainNews);
        } catch(error){
            res.status(403)
            throw new Error("Action failed");
        }
    });