1

I want to upload multiple images from my reactjs frontend using DropZone. I have configured the backend to upload multiple images but everytime i try to upload images no images get uploaded and i recieve an empty array for the images.

in my upload.js file the getResult() function always returns this

return res.send(`You must select at least 1 image.`);

Find this image to view the data in my payload

How can i fix this?

Here is my code:

NodeJs Backend upload.js:

const multer = require("multer");
const sharp = require("sharp");

const multerStorage = multer.memoryStorage();

const multerFilter = (req, file, cb) => {
    if (file.mimetype.startsWith("image")) {
        cb(null, true);
    } else {
        cb("Please upload only images.", false);
    }
};

const upload = multer({
    storage: multerStorage,
    fileFilter: multerFilter
});

const uploadFiles = upload.array("images", 10);

const uploadImages = (req, res, next) => {
    uploadFiles(req, res, err => {
        if (err instanceof multer.MulterError) {
            if (err.code === "LIMIT_UNEXPECTED_FILE") {
                return res.send("Too many files to upload.");
            }
        } else if (err) {
            return res.send(err);
        }

        next();
    });
};

const resizeImages = async (req, res, next) => {
    if (!req.files) return next();

    req.body.images = [];
    await Promise.all(
        req.files.map(async file => {
            const filename = file.originalname.replace(/\..+$/, "");
            const newFilename = `bezkoder-${filename}-${Date.now()}.jpeg`;

            await sharp(file.buffer)
                .resize(640, 320)
                .toFormat("jpeg")
                .jpeg({ quality: 90 })
                .toFile(`images/${newFilename}`);

            req.body.images.push(newFilename);
        })
    );

    next();
};

const getResult = async (req, res, next) => {
    console.log(req.body.images)
    if (req.body.images.length <= 0) {
        return res.send(`You must select at least 1 image.`);
    }

    const images =  JSON.parse(req.body.images)
        .map(image => "" + image + "")
        .join("");

    // return res.send(`Images were uploaded:${images}`);
    next();
};

module.exports = {
    uploadImages: uploadImages,
    resizeImages: resizeImages,
    getResult: getResult
};

then in my controller projects.js:

router.post('/create-project', [upload.uploadImages, upload.resizeImages, upload.getResult], (req, res) => {
          //my code here
})

ReactJs Frontend createProjects:

const [data, setData] = useState({
    projectName: '',
    projectDesc: ''
  });
  const [startDate, setStartDate] = useState(new Date());
  const [endDate, setEndDate] = useState(new Date());
  const [selectedFiles, setSelectedFiles] = useState([]);

  const handleChange = (e) => {
    setData({ ...data, [e.target.name]: e.target.value });
  }

  const startDateChange = date => {
    setStartDate(date);
  };

  const endDateChange = date => {
    setEndDate(date);
  };


  function handleAcceptedFiles(files) {
    files.map(file =>
      Object.assign(file, {
        preview: URL.createObjectURL(file),
        formattedSize: formatBytes(file.size)
      })
    );
    setSelectedFiles(files);
  }

  function formatBytes(bytes, decimals = 2) {
    if (bytes === 0) return "0 Bytes";
    const k = 1024;
    const dm = decimals < 0 ? 0 : decimals;
    const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];

    const i = Math.floor(Math.log(bytes) / Math.log(k));
    return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i];
  }

  const createProject = async (e) => {
    e.preventDefault();
    const formData = new FormData()
    formData.append("projectName", data.projectName);
    formData.append("projectDesc", data.projectDesc);
    formData.append("startDate", JSON.stringify(startDate));
    formData.append("projectName", JSON.stringify(endDate));
    formData.append("images", JSON.stringify(selectedFiles));
    addProject(formData);
  }

return (


                       <Form>
                        <Dropzone
                          onDrop={acceptedFiles => {
                            handleAcceptedFiles(acceptedFiles);
                          }}
                          name="images"
                        >
                          {({ getRootProps, getInputProps }) => (
                            <div className="dropzone">
                              <div
                                className="dz-message needsclick"
                                {...getRootProps()}
                              >
                                <input {...getInputProps()} name="images" type="file" multiple />
                                <div className="dz-message needsclick">
                                  <div className="mb-3">
                                    <i className="display-4 text-muted bx bxs-cloud-upload" />
                                  </div>
                                  <h4>Drop files here or click to upload.</h4>
                                </div>
                              </div>
                            </div>
                          )}
                        </Dropzone>
                       </Form>
)

2 Answers 2

0

Try appending each file, instad of stringifying them:

//... 

formData.append("projectName", JSON.stringify(endDate));

for (const file of selectedFiles) {
    
    formData.append("images", file);

}

addProject(formData);

Also, it seems you're trying to add custom properties to the file, this won't work, try something like this: File object with custom properties

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

3 Comments

Thank you for your answer, but how will the backend handle the images and upload them to my images folder? i am not trying to add custom properties, these are the properties that get added when i upload the files in the dropzone
Thank you, your solution worked, but for some reason selectedFiles always contains 1 image file even if i put more than 1 image. i keep adding 2 or 3 images but when i log the formdata its only 1 image. why is that?
can you log selectedFiles/files. you probably need to modify handleChange to include e.target.files: stackoverflow.com/questions/68213700/…
0

I finally found the solution that worked correctly for my usecase i had to use the forEach() function to append each file to the formData()

const formData = new FormData()
    formData.append("projectName", data.projectName);
    formData.append("projectDesc", data.projectDesc);
    formData.append("comments[]", comment);
    formData.append("startDate", startDate.toISOString());
    formData.append("dueDate", endDate.toISOString());
    selectedFiles.forEach((images, index) => {
      formData.append(`images`, images);
    });
    addProject(formData);

1 Comment

..and how this is different from the for..of loop.? See: Should one use for-of or forEach when iterating through an array?

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.