简体   繁体   中英

How to send a Png or Jpeg image as binary to node.js server from react app?

I am trying to send a file from a react web app to node.js server.

I am starting off with an html input of type file, Once user uploads their file, I trigger a post request to my Node.js server.

Once in my Node.js server, I need to send a post request to a 3rd party API (Twilio)

3rd party API needs my file as binary data.

Note: from docs "The body or content of the POST must be the file itself in binary format."

Here is React component below

import React, { Fragment, useState, useRef } from "react";
import { Col, Button, Media, Card, CardBody } from "reactstrap";
import { useSelector, useDispatch } from "react-redux";
import { attachMMS } from "../../../actions/index";
import cloudUpload from "../../../assets/img/icons/cloud-upload.svg";

const InsertMMS = ({
  showFileModal,
  setShowFileModal,
  showInsertMMSModal,
  setShowInsertMMSModal,
  uploadMMS,
  setUploadMMS,
}) => {
  const InputFile = useRef(null);
  const [userMMS, setUserMMS] = useState();
  const [highlighted, setHighlighted] = useState(false);
  const dispatch = useDispatch();

  const attachMMSCreate = useSelector((state) => state.attachMMSCreate);

  const manualMMSUpload = (e) => {
    console.log("manualMMSUpload ran");
    e.preventDefault();
    if (e.target.files[0].name) {
      console.log("file", e.target.files[0]);
      getBinary(e.target.files[0]);
      setUserMMS(e.target.files[0].name);
    }
  };

  const getBinary = (file) => {
    const reader = new FileReader();
    reader.readAsArrayBuffer(file);
    reader.onload = () => {
      setUploadMMS({ file: file, fileType: file.type });
    };
  };

  const handleInputChange = ({ value, name }) =>
    setUploadMMS({ ...uploadMMS, [name]: value });

  const onButtonClick = (e) => {
    e.preventDefault();
    InputFile.current.click();
  };

  return (
    <Fragment>
      <Card>
        <CardBody className="fs--1 font-weight-normal p-4">
          <h4 className="mb-1 mt-3 text-center"></h4>
          <div>
            <input
              type="file"
              onChange={(e) => manualMMSUpload(e)}
              accept=".gif, .png, .jpeg"
              ref={InputFile}
              className="d-none"
            />
            <div
              className={`mb-2 p-3 border-dashed border-2x border-300 bg-light rounded-soft text-center cursor-pointer ${
                highlighted ? " border-800" : ""
              }`}
              onClick={(e) => onButtonClick(e)}
              onDragEnter={() => {
                setHighlighted(true);
              }}
              onDragLeave={() => {
                setHighlighted(false);
              }}
              onDragOver={(e) => {
                e.preventDefault();
              }}
              onDrop={(e) => {
                e.preventDefault();
              }}
            >
              <Fragment>
                <Media className=" fs-0 mx-auto d-inline-flex align-items-center">
                  <img src={cloudUpload} alt="" width={25} className="mr-2" />
                  <Media>
                    <p className="fs-0 mb-0 text-700">
                      {userMMS ? userMMS : "Upload your File"}
                    </p>
                  </Media>
                </Media>
                <p className="mb-0 w-75 mx-auto text-500">
                  Supports: .gif, .png, .jpeg{" "}
                </p>
              </Fragment>
            </div>
          </div>
          <p className="fs-0 text-center">multi-media message</p>
          <Col className="text-center">
            <Button
              disabled={!uploadMMS}
              color="primary"
              onClick={() => {
                return (
                  dispatch(attachMMS(uploadMMS)),
                  setShowInsertMMSModal(!showInsertMMSModal),
                  setShowFileModal(!showFileModal)
                );
              }}
              className="my-3 text-white"
            >
              {attachMMSCreate.loading ? "...processing" : "Attach MMS"}
            </Button>
          </Col>
        </CardBody>
      </Card>
    </Fragment>
  );
};

export default InsertMMS;

Here is the Action being called from React making the POST request to Node.js server

export const attachMMS = (uploadMMS) => async (dispatch) => {
  console.log("body of attachMMS in actions", uploadMMS);
  try {
    dispatch({ type: ATTACH_MMS_CREATE_REQUEST });
    await axios({
      url: "http://localhost:5000/mms",
      method: "POST",
      data: uploadMMS.file,
      withCredentials: true,
    }).then((res) =>
      dispatch({ type: ATTACH_MMS_CREATE_SUCCESS, payload: res })
    );
  } catch (error) {
    console.log(error);
    dispatch({
      type: ATTACH_MMS_CREATE_FAIL,
      payload:
        error.message && error.response.data.message
          ? error.response.data.message
          : error.message,
    });
  }
};

Here is the Node.js route being triggered

module.exports = (router) => {
  router.route("/mms").post(twilCtrl.createMediaMMSResource);
};

Here is the Node.js function being called

const createMediaMMSResource = async (req, res) => {
  try {
    const { subActServiceSid, subActAuthToken, convoServiceSid } = req.user;
    let mediaResource;

    await axios({
      url: `https://mcs.us1.twilio.com/v1/Services/${convoServiceSid}/Media`,
      method: "POST",
      data: req.body, // this needs to be the file itself in binary format
      headers: {
        "Content-Type": "image/png",
      },
      auth: { username: subActServiceSid, password: subActAuthToken },
      withCredentials: true,
    }).then((res) => {
      console.log(
        "res from first post request to twilio with fileData",
        res.data
      );
      return (mediaResource = {
        mediaUrl: res.data.url,
        serviceSid: res.data.service_sid,
        mediaSid: res.data.sid,
      });
    });
    res.status(201).json(mediaResource);
  } catch (err) {
    console.log(err);
  }
};

Here is the response I get from client....

Does anyone see the issue? I cant seem to understand how to send "Binary" data to node.js server.....

在此处输入图像描述

It is possible to send binary data from the browser which you can handle in Express via the express.raw() middleware however I would do the following instead...

  1. Upload the file to your Express service via FormData . This is typically how browsers perform binary uploads and utilities like Axios make it even easier.

     const manualMMSUpload = (e) => { if (e.target.files.length > 0) { const file = e.target.files[0]; setUploadMMS({ file, fileType: file.type }); setUserMMS(file.name); } };
     // export const attachMMS = (uploadMMS) => async (dispatch) => { // ... // use axios.postForm() const res = await axios.postForm("http://localhost:5000/mms", { file: uploadMMS.file }, { withCredentials: true }); // do you actually need cookies? dispatch({type: ATTACH_MMS_CREATE_SUCCESS, payload: res}) // ...

    FYI you do not need withCredentials unless you're relying on cookies.

    If you're on an older version of Axios without postForm , use the following

    const data = new FormData(); data.append("file", uploadMMS.file); const res = await axios.post("http://localhost:5000/mms", data);
  2. Handle the file upload in your Express service using the Multer or express-fileupload middleware.

     const fileUpload = require('express-fileupload'); router.route('/mms').post(fileUpload(), twilCtrl.createMediaMMSResource)
  3. Pass the uploaded file buffer through to the upstream API.

     const response = await axios.post( `https://mcs.us1.twilio.com/v1/Services/${convoServiceSid}/Media`, req.files.file.data, // here's the buffer { auth: { username: subActServiceSid, password: subActAuthToken }, headers: { "content-type": req.files.file.mimetype, }, } );

    This should upload the file data buffer as raw binary data.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM