is there a way i can handle file upload in react - node.js

iam trying to submit these fields to node server from react i can submit rest of the input fields and select options but i cannot upload the photo plus i can upload all fields (including the photo) to the server using POSTMAN
// fields to submit to node server
state = {
data: {
fullName: '',
email: '',
phoneNumber: '',
branchId: '',
jobId: '',
salary: '',
image: '',
// startDate: '',
},
// onChange handler
handleChange = ({ currentTarget: input }) => {
//clone the errors
const errors = { ...this.state.errors }
//validate the input
const errorMessage = this.validateProperty(input)
//
if (errorMessage) errors[input.name] = errorMessage
//
else delete errors[input.name]
//clone the data
const data = { ...this.state.data }
//override the state data with the input value
data[input.name] = input.value
// update the state with the data and errors
this.setState({ data, errors })
}
// submit or onClick handler
doSubmit = async () => {
const { data } = this.state
const fd = new FormData()
fd.append('fullName', data.fullName)
fd.append('email', data.email)
fd.append('phoneNumber', data.phoneNumber)
fd.append('branchId', data.branchId)
fd.append('jobId', data.jobId)
fd.append('salary', data.salary)
fd.append('image', data.image)
// fd.append()
await saveEmployee(fd)
this.props.history.push('/employees')
}

I'm guessing that the image input is a file field?
You are now storing the input#value of the file field but you're actually looking for the input#files[0] value. This contains the reference to the actual file and can be appended to the form.
handleChange = ({ currentTarget: input }) => {
//clone the errors
const errors = { ...this.state.errors }
//validate the input
const errorMessage = this.validateProperty(input)
//
if (errorMessage) errors[input.name] = errorMessage
//
else delete errors[input.name]
//clone the data
const data = { ...this.state.data }
//override the state data with the input value
data[input.name] = input.type === 'file' ? input.files[0] : input.value
// update the state with the data and errors
this.setState({ data, errors })
}

This can be achieved in the following way:
In Server use "multer" to handle uploaded file:
https://expressjs.com/en/resources/middleware/multer.html
Now create an endpoint to handle your file upload.
For instance(server code):
// Import Multer
const multer = require("multer");
// Express
let app = express();
// upload destination
const uploadLocation = multer({ dest: "uploads/" });
// Endpoint
app.post(
"/upload",
// Here pass the name of file that you've set in FormData. In your
// case its "image", as your appending file data as image in your
// code.
uploadLocation.single("image"),
async (req, res) => {
// get file from req
const file = req.file;
// This is your uploaded file
console.log(file);
// You can handle other data here...
res.status(200).send({ path: `uploads/${file.filename}` });
}
);
Now use this endpoint to send your from data.
For instance (client side code in Javascript):
const data = await fetch(
`${process.env.REACT_APP_SERVER_URL}/upload`,
{
method: "POST",
credentials: "include",
headers: {},
// Send your form data like this to server
body: formData,
}
);
const { path } = await data.json();
// This is your path of uploaded file
console.log(path)

Related

Remix Run UploadHandler Using Data for WhatsApp API Media Upload

I am attempting to use a form in Remix to add a file and then upload that file to WhatsApp using their Cloud API Media Upload endpoint. Below is my initial code within the action. The current error I am receiving is message: '(#100) The parameter messaging_product is required.. I feel like this error may be misleading based off the form data I have appended with the "messaging_product".
export async function action({ request, params }: ActionArgs) {
const uploadHandler = unstable_composeUploadHandlers(
async ({ name, contentType, data, filename }) => {
const whatsAppPhoneId = process.env.WHATSAPP_PHONE_ID;
const whatsAppToken = process.env.WHATSAPP_ACCESS_TOKEN;
const dataArray1 = [];
for await (const x of data) {
dataArray1.push(x);
}
const file1 = new File(dataArray1, filename, { type: contentType });
const graphApiUrl = `https://graph.facebook.com/v15.0/${whatsAppPhoneId}/media`;
const formData = new FormData();
formData.append("file", file1);
formData.append("messaging_product", "whatsapp");
formData.append("type", contentType);
try {
const imageMediaResponse = await fetch(graphApiUrl, {
method: "POST",
headers: {
Authorization: `Bearer ${whatsAppToken}`,
"Content-Type": "multipart/form-data",
},
body: formData,
});
const imageMedia = await imageMediaResponse.json();
return imageMedia?.id;
} catch (error) {
console.error(error);
}
const whatsAppMediaId = await uploadWhatsAppImageMedia(
whatsAppPhoneId,
whatsAppToken,
data,
filename,
contentType
);
}
);
const formData = await unstable_parseMultipartFormData(
request,
uploadHandler
);
}

why I get an empty request body express

Router
router.patch("/fav/:id", getFav);
controller
const getFav = async (req, res) => {
const { favToadd } = req.body;
const { id } = req.params;
try {
const Users = await User.findById(id);
console.log("body : ", req.body); // body : {}
const fav = await User.findByIdAndUpdate(id, {
$set: { favorite: [...Users.favorite, favToadd] },
});
res.send(fav);
} catch (error) {
console.log("err ", error.message);
}
};
//Function to add favorites
const response = await fetch(
`http://localhost:4000/api/user/fav/${currentUser._id}`,
{
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ prd: product.props }),
}
);
};
the item gets saved to MongoDB as null and then I take look at the req body it's empty,
my goal is to add an object to the favorite properties in MongoDB document
but it gets saved as null
try different methods but nothing works I don't think I get a body from the patch request
don't know if the problem with it or in the communication between the front and the back end it's
my first app so hopefully, the problem is in this line of code not in another part,
when I send it from postman with raw JSON it Works and gets saved at MongoDB with the properties I set

How can I upload an image and form data as well on a single button click?

I am making a practice project in MERN stack and wanted to upload images from react js with the form on single button click, so I needed to call two apis on just one button click. But I am having errors that's why I am unable to upload image and form data as well.
My react js code here:
const URL = "http://localhost:2040/add_recipe";
const formData = new FormData();
formData.append("recipe_image", selectedFile);
let config = {
headers: {
"Content-Type": "multipart/form-data",
authorization: JSON.parse(localStorage.getItem("token")),
},
};
axios
.post(URL, formData, config)
.then((response) => {
console.log("Image uploaded successfully" + response);
})
.catch((error) => {
console.log("Error while uploading image" + error);
});
and here is my backend api:
const Recipe = require("../models/Recipe");
const fs = require("fs");
let filePath = "";
const AddRecipe = async (req, res) => {
if (req.file) {
filePath = req.file.path;
}
console.log("filepath: " + filePath);
let recipee = await Recipe.findOne({ name: req.body.name });
if (recipee) {
res.json({ Response: "Recipe already exists!" });
} else {
if (
req.body.category &&
req.body.ptime &&
req.body.name &&
req.body.noOfPeople &&
req.body.shortDesc &&
req.body.recipe
) {
let recipe = await Recipe.create({
category: req.body.category,
ptime: req.body.ptime,
name: req.body.name,
noOfPeople: req.body.noOfPeople,
shortDesc: req.body.shortDesc,
recipe: req.body.recipe,
avatar: {
data: fs.readFileSync(filePath),
contentType: "image/png",
},
});
let result = await recipe;
console.log(filePath + " .......path");
if (result.name) {
res.json({ Response: "Recipe added successfully!" });
} else {
res.json({ Response: "Recipe not added!" });
}
}
}
};
module.exports = { AddRecipe };
This is how I called the api with multer already setup
app.post("/add_recipe", verifyToken, upload.single("recipe_image"), AddRecipe);
I found the answer, actually I had to sent all data using FormData inside the axios request and its content-type would be multipart/form-data.
So, the request should be one because url is same and we can send form data and image as well using FormData append method and on backend we can get image as req.file and data as req.body.*
That's all!

Upload image to s3 bucket - react native and node js

Within my app a user can select a profile image and i would like that image to be uploaded to an s3 bucket when the user saves their profile data
I pass the image data (and json, which consists of name, email, telephone for example) from my app to an express server and upload there
At present I can pass the image data (the url it seems at present) to an s3 bucket and it saves
I don't think i'm actually saving the image itself though, as when downloading from s3 (manually) and trying to open on my mac it states it may be damaged and i cannot see the image
Feel daft for asking but how do i actually upload the image itself? Thanks
React Native Side
const handleFormSubmit = formData => {
const jsonData = JSON.stringify({
...formData,
});
// Handle profile image
if (imageProps && imageProps.uri) {
const data = new FormData();
data.append('formBody', jsonData);
data.append('image', {
uri:
Platform.OS === 'android'
? imageProps.uri
: imageProps.uri.replace('file://', ''),
type: imageProps.type,
name: imageProps.fileName,
});
sendRequest(data);
} else {
sendRequest(jsonData);
}
};
const sendRequest = data => {
let responseData;
fetch('http://localhost:8080/users/api/update_user_profile', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
body: data,
})
.then(response => {
responseData = response;
return response.json();
})
.then(jsonData => {
console.log(jsonData)
})
.catch(error => {
console.log(error)
});
};
Server Side
const s3 = new AWS.S3({
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
});
// Setting up S3 upload parameters
const params = {
Bucket: 'bucket-folder',
ACL: 'public-read',
Key: req.files.image.name,
Body: req.files.image.path
};
const stored = await s3.upload(params).promise();
You can use Multer for uploading files to s3.
const multer = require('multer');
const AWS = require('aws-sdk');
const uniqid = require('uniqid');
const storage = multer.memoryStorage();
const upload = multer({ storage });
// ? Posts new file to amazon and saves to db
router.post(
'/:id',
upload.single('attachment'),
async (req, res) => {
const unique = uniqid.time();
const { file } = req;
const { filePath } = req.body;
const { id } = req.params;
const s3FileURL = process.env.AWS_UPLOADED_FILE_URL;
const region = process.env.AWS_REGION;
const secretAccessKey = process.env.AWS_SECRET_ACCESS_KEY;
const accessKeyId = process.env.AWS_ACCESS_KEY_ID;
const Bucket = process.env.AWS_BUCKET_NAME + '/' + filePath;
const Key = `${id}/${unique}-${file.originalname}`;
const Body = file.buffer;
const ContentType = file.mimetype;
const ACL = 'public-read';
const s3bucket = new AWS.S3({
accessKeyId,
secretAccessKey,
region,
});
const params = {
Bucket,
Key,
Body,
ContentType,
ACL,
};
s3bucket.upload(params, async (err, data) => {
if (err) {
res.status(500).json({ error: true, Message: err });
} else {
console.log(params);
const newFileUploaded = {
description: req.body.description,
fileLink: `${s3FileURL}${filePath}/${id}/${unique}-${file.originalname}`,
s3_key: params.Key,
};
try {
const response = await postFile({
name: req.body.name,
attachment: newFileUploaded,
alt: req.body.alt,
user: req.body.user,
relatedID: req.body.relatedID,
});
res.status(200).json({
message: response.message,
success: response.success,
result: response.result,
});
} catch (e) {
res.status(500).json({
message:
'File upoladed but Db couldnt saved request (upload by ID)',
success: false,
result: [],
});
}
}
});
}
);

Uploading blob/file in react-native, contents is empty

I am able to succesfully upload a blob with proper contents from my web browser, but when I do it from react-native, the upload file is empty. Here is the code:
async function doit() {
const data = new FormData();
data.append('str', 'strvalue');
data.append(
'f',
new File(['foo'], 'foo.txt', {type: 'text/plain'}),
);
await fetch('http://localhost:3002/upload', {
method: 'POST',
body: data
});
}
However doing this same code from react-native, it uploads, but the file is empty.
Here is the node.js server I am using to test this. Loading http://localhost:3002 gives you a button called "upload it". Clicking it does the upload from the web. Screenshots of results are below.
var multiparty = require('multiparty');
var http = require('http');
http
.createServer(function (req, res) {
if (req.url === '/upload' && req.method === 'POST') {
console.log('multipart here');
var form = new multiparty.Form();
form.parse(req, function (err, fields, files) {
console.log(require('util').inspect({ fields, files }, false, null, true));
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ bar: true }));
});
return;
}
console.log('here');
// show a file upload form
res.writeHead(200, { 'content-type': 'text/html' });
res.end(
`
<script>
async function doit() {
const data = new FormData();
data.append('str', 'strvalue');
data.append(
'f',
// new File([new Blob(['asdf'], {type : 'text/plain'})], 'filename.txt'),
new File(['foo', 'what', 'the', 'hell'], 'foo.txt', {type: 'text/plain'}),
);
const res = await fetch('http://localhost:3002/upload', {
method: 'POST',
body: data
});
console.log(JSON.stringify(res, null, 4));
}
document.addEventListener('DOMContentLoaded', () => {
document.getElementById('b').addEventListener('click', doit, false)
}, false);
</script>
<button type="button" id="b">upload it</button>
`
);
})
.listen(3002);
From web browser we see the node server logs this, notice file size is 14.
However from react-native we see file size is 0:
I faced the same problem recently while posting an image from a react-native app to a server. However, I was able to make it work by appending the name and type of the file to the formData instance.
Here, the uri argument to uploadImageAsync is passed as a route parameter from the previous screen.
const postShoutHandler = async () => {
setShoutUploadStatus("Started Upload");
const response = await uploadImageAsync(route.params.captures);
const uploadResult = await response.json();
if (uploadResult === "Upload successful") {
setShoutUploadStatus("Success");
navigation.navigate("Home");
} else {
setShoutUploadStatus("Failed");
}
};
/* <--Upload image function --> */
const uploadImageAsync = (uri: string) => {
const apiUrl = "https://www.yourserver.com/image";
let uriParts = uri.split(".");
let fileType = uriParts[uriParts.length - 1];
let formData = new FormData();
formData.append("img", {
uri,
name: `photo.${fileType}`,
type: `image/${fileType}`,
});
formData.append("description", "HEY");
let options = {
method: "POST",
body: formData,
headers: {
Accept: "application/json",
"Content-Type": "multipart/form-data",
Authorization: "Bearer " + accessToken,
},
};
return fetch(apiUrl, options);
};
/* <--Upload image function --> */
Here is the Image configuration.
const photoData = await camera.takePictureAsync({
base64: true,
exif: false,
});

Resources