Multer zip file upload works through Postman, but not in Next.js client side

Viewed 41

When I make a request to my backend using Postman everything works just fine, but when I try to make the same request in my client-side code req.file is not populated, and neither is req.body.

My API route: (Which should be okay)

router.route('/:id')
  .put(upload.single('zip'), asyncHandler(async (req, res) => {
    // req.file = undefined
    // I left out all the logic as how I handle the data is irrelevant,
    // I just need req.file to be populated in my client side request
  });

The Postman request that comes through just fine, as expected: https://share.mirasaki.dev/viBO2/QUYuhEWa94.png

The client/frontend code:

  // State management
  const [ file, setFile ] = useState(undefined);
  const [ showErrorFeedback, setShowErrorFeedback ] = useState(false);
  const [ submitState, setSubmitState ] = useState({
    error: null,
    message: null
  });

  // Save file in state on change
  const handleFileChange = (e) => {
    e.target.files && setFile(e.target.files[0]);
  };

  // Handle the form submit
  const handleSubmit = async (event, url) => {
    event.preventDefault();

    // Return and show error if no file was uploaded
    if (!file) {
      setSubmitState({
        error: 'No file was uploaded, this action has been cancelled'
      });
      setShowErrorFeedback(true);
      return;
    }

    // Constructing the form data
    const formData = new FormData();
    formData.append('zip', file); // HTML5 File object from state

    // PUT the form data
    try {
      const response = await axios.put(url, formData);
    } catch (err) {
      setSubmitState({ error: err.message });
      setShowErrorFeedback(true);
    }
  };

  return (
    <Container>
      <div><DashboardNavigation guild={props.guild}/></div>

      {/* Feedback */}
      {submitState.error && <Alert show={showErrorFeedback} variant="danger">
        <Alert.Heading>Encountered an error</Alert.Heading>
        <p>{submitState.error}</p>
        <div className="d-flex justify-content-end">
          <Button onClick={() => setShowErrorFeedback(false)} variant="outline-dark">
            Close
          </Button>
        </div>
      </Alert>}

      {/* Zip file uploads */}
      {zipUploads.map((zipCfg) => {
        const prop = props[zipCfg.propId];
        return (
          <Form
            key={zipCfg.endpoint}
            className={styles.form}
            // action={`${process.env.NEXT_PUBLIC_BACKEND_URL}/api/${zipCfg.endpoint}/${router.query.id}`}
            // method='put'
          >
            <h2>{zipCfg.name}</h2>
            <p>{zipCfg.description}</p>

            <Form.Group controlId={`control-${zipCfg.name}`}>
              <Form.Label>{zipCfg.path}</Form.Label>
              <Form.Control
                required
                type="file"
                name='zip'
                accept={zipCfg.accept}
                onChange={handleFileChange}
              />
            </Form.Group>

            <Button
              variant="primary"
              type="button"
              className={styles.formSubmitButton}
              onClick={(event) => handleSubmit(
                event,
                `${process.env.NEXT_PUBLIC_BACKEND_URL}/api/${zipCfg.endpoint}/${router.query.id}`
              )}
            >
              Submit
            </Button>
            {prop && <p className={styles.formFooter}>Updated: {prop.updatedAt}</p>}
          </Form>
        );
      })}

      {/* JSON OUTPUT [DEV] */}
      <pre style={{ color: 'white' }}>
        {JSON.stringify(props, null, 2)}
      </pre>
    </Container>
  );

What I've tried: I've tried so many different approaches. I've tried Axios and node:fetch to make the request. I've tried setting the Content-Type header to multipart/form-data, form-data, and application/x-www-form-urlencoded (and leaving it undefined) with every combination of Axios & node:fetch. I've tried FormData.set, FormData.append, I've tried constructing a new HTML5 File object (new File(file, 'zip', { name: 'zip' })) to try and overwrite file names dynamically, I thought maybe that was the issue instead of the form data key name

I've been banging my head at this for hours to no avail. I've searched google far and wide, but can't find any solution

Yes, I've read through the multer questions on Stack Overflow, none of which resolved my issue.

I'm at a loss here, I am completely clueless

0 Answers
Related