How do I save uploaded file with other fileds using MERN? - node.js

I'm trying to save a uploaded file with other fields like 'title', 'description',.
I'm able to save data using postman but I'm not able to save data from Reacjs form with Redux.
This is how my backend received the data:
const department = req.body.department;
const title = req.body.title;
const description = req.body.description;
const file = req.files.file;
I can save from postman but not react form.
This is my react form:
<form onSubmit={(e) => onSubmit(e)} encType='multipart/form-data'> ... some fields ... </form/
This is my react state and submission form data:
const [file, setFile] = useState('');
const [filename, setFilename] = useState('Choose file...');
const [bodyData, setBodyData] = useState({
title: '',
department: '',
});
const { title, department } = bodyData;
const onChange = (e) =>
setBodyData({ ...bodyData, [e.target.name]: e.target.value });
This is my Redux
// Create document file for specific patient
export const addFile = (form, id) => async (dispatch) => {
console.log(form.bodyData);
try {
const config = {
headers: { 'Content-Type': 'application/json' },
};
const res = await axios.put(
`/api/document/file/${id}`,
(form.bodyData, form.formData),
config
);
dispatch({
type: ADD_DOCUMENT_FILE,
payload: res.data,
});
dispatch(setAlert('Successfully Uploaded Patient Document', 'success'));
} catch (err) {
const errors = err.response.data.errors;
if (errors) {
errors.forEach((error) => dispatch(setAlert(error.msg, 'danger')));
}
dispatch({
type: DOCUMENT_FILE_ERROR,
payload: { msg: err.response.statusText, status: err.response.status },
});
}
};
const onChangeFile = (e) => {
setFile(e.target.files[0]);
setFilename(e.target.files[0].name);
};
const onSubmit = async (e) => {
e.preventDefault();
const formData = new FormData();
formData.append('file', file);
const collect = { formData, bodyData };
addFile(collect, match.params.id);
};

It seems like you are somewhat confused about how to handle this form submission.
In your onSubmit function, you reference a variable file which isn't defined in that scope, also you return formData, bodyData but bodyData isn't defined, and formData and bodyData are synonyms for the same data, so I don't know why you need both.
Consider this question for an example solution: How to post a file from a form with Axios

Yes. Finally I figured it out.
I changed my submit to this code:
const onSubmit = async (e) => {
e.preventDefault();
const formData = new FormData();
formData.append('title', title);
formData.append('department', department);
formData.append('file', file);
addFile(formData, match.params.id);
};
and the Redux I changed to this code:
export const addFile = (formData, id) => async (dispatch) => {
// console.log(form.bodyData);
try {
const config = {
// headers: { 'Content-Type': 'multipart/form-data' },
headers: { 'Content-Type': 'application/json' },
};
const res = await axios.put(
`/api/document/file/${id}`,
// { title: form.bodyData.title, department: form.bodyData.department },
formData,
config
);
dispatch({
type: ADD_DOCUMENT_FILE,
payload: res.data,
});
dispatch(setAlert('Successfully Uploaded Patient Document', 'success'));
} catch (err) {
const errors = err.response.data.errors;
if (errors) {
errors.forEach((error) => dispatch(setAlert(error.msg, 'danger')));
}
dispatch({
type: DOCUMENT_FILE_ERROR,
payload: { msg: err.response.statusText, status: err.response.status },
});
}
};

Related

How to update data in mongodb database and show in ui

Client-side code...!!!
I want to update the quantity by clicking the Delivered button...
In this section have quantity that i'm show bring database.
const [reload, setReload] = useState(true);
const [stock, setStock] = useState({});
const { _id, quantity } = stock;
useEffect(() => {
const url = `http://localhost:5000/stock/${inventoryId}`;
fetch(url)
.then((res) => res.json())
.then((data) => setStock(data));
}, [reload]);
In this function I want to update quantity that i'm decrising by clicking button
const handleDelivered = () => {
const updateQuantity = parseInt(quantity) - 1; // *actually I want to decrise quantity by clicking Delivered button*
const url = `http://localhost:5000/stock/${inventoryId}`;
fetch(url, {
method: 'PUT',
headers: {
'content-type': 'application/json',
},
body: JSON.stringify(updateQuantity), // *I don't know it's right or wrong and it's so important*
})
.then((res) => res.json())
.then((data) => {
setReload(!reload);
setStock(data);
console.log(data);
});
};
return <button onClick={handleDelivered}>Delivered</button>
Server-side code...!!!
On server-side I want to update just the quantity value...
app.put('/stock/:id', async (req, res) => {
const id = req.params.id;
const updateQuantity = req.body;
const filter = { _id: ObjectId(id) };
const options = { upsert: true };
const updateDoc = {
$set: {
updateQuantity, // *I'm not sure how to set it database. But I want to update quantity value*
},
};
const result = await stockCollection.updateOne(filter, updateDoc, options);
res.send(result);
});
You need to set the updatedQuantity on the "quantity" field of the database. You need to change the "updateDoc" object like this in your server-side code:
const updateDoc = {
$set: {
quantity: updateQuantity,
},
};
Hope this should work.
Client-side code
const handelUpload = (e) => {
e.preventDefault();
const storeQuantity = parseInt(product?.quantity);
const newQuantity = parseInt(e.target.quantity.value);
const quantity = storeQuantity + newQuantity;
if (quantity > 0) {
fetch(`http://localhost:5000/stock/${inventoryId}`, {
method: "PUT",
headers: {
"Content-type": "application/json; charset=UTF-8",
},
body: JSON.stringify({ quantity }),
})
.then((response) => response.json())
.then((data) => {
console.log("success", data);
alert("users added successfully!!!");
e.target.reset();
setIsreload(!isReload);
});
} else {
console.log("input number");
}
};
Server side code api
app.put("/stock/:id", async (req, res) => {
const id = req.params.id;
const updatedProduct = req.body;
console.log(updatedProduct.quantity);
console.log(typeof updatedProduct.quantity);
const filter = { _id: ObjectId(id) };
const options = { upsert: true };
const updatedDoc = {
$set: {
// name: updatedProduct.name,
// email: updatedProduct.email,
quantity: updatedProduct.quantity,
// price: updatedProduct.price,
},
};
const result = await stockCollection.updateOne(filter,updatedDoc,options
);
res.send(result);
});

I get undefined when reading my response but there is a response in React.js

I can't figure it out, the answer comes in the network table but when I want to console.log it, this will display undefined. Do you have any idea why? I attach the pictures and the code.
Here is a image with my codes and response
Here is the code - first one is where I send the response. As I said, it's going well on network tab, I get a 200 status.
export const getAccountStatus = async (req, res) => {
const user = await User.findById(req.user._id).exec();
const account = await stripe.accounts.retrieve(user.stripe_account_id);
// console.log("user account retrieve", account);
const updatedUser = await User.findByIdAndUpdate(
user._id,
{
stripe_seller: account
},
{ new: true }
)
.select("-password")
.exec();
console.log(updatedUser);
res.send(updatedUser);
};
Here is the page where i want to console.log it:
const StripeCallback = ({ history }) => {
const { auth } = useSelector(state => ({ ...state }));
const dispatch = useDispatch();
useEffect(() => {
if (auth && auth.token) accountStatus();
}, [auth]);
const accountStatus = async () => {
try {
const res = await getAccountStatus(auth.token);
console.log(res);
} catch (err) {
console.log(err);
}
};
return <div>test</div>;
};
Ang here is the Axios.post (which is working well as I know):
export const getAccountStatus = async token => {
await axios.post(
`${process.env.REACT_APP_API}/get-account-status`,
{},
{
headers: {
Authorization: `Bearer ${token}`
}
}
);
};
Thank you!
getAccountStatus doesn't have a return statement, so res in const res = await getAccountStatus(auth.token); will always be undefined.
export const getAccountStatus = async token => {
return axios.post( // <----- added return
`${process.env.REACT_APP_API}/get-account-status`,
{},
{
headers: {
Authorization: `Bearer ${token}`
}
}
);
};

how to update context state in react

I am having a problem that when user upload their profile image it did not change, user have to log out and log back in to make a change complete.
Here is my back end how to get image from client and store it on cloudinary:
profilesController.js:
exports.updateAvatar = async (req, res) => {
// Find user with matching token
// const updates = [];
const updateUserAvatar = await models.User.findOne({
where: {
id: req.id,
},
});
// Was user found?
if (updateUserAvatar === null) {
return res.status(200).json({
validationErrors: {
errors: [
{
msg: "Reset is invalid or has expired.",
},
],
},
});
}
// Update user with new info
models.User.update(
{
picture: req.imageUrl,
},
{
where: {
id: updateUserAvatar.dataValues.id,
},
}
);
console.log(updateUserAvatar);
At the console it should gave me a new image url but instead it just keep the old image url
Here is my profilesAPI where my route is:
router.post('/upload/image', function (req, res, next) {
const dUri = new Datauri();
const dataUri = (req) => dUri.format(path.extname(req.name).toString(), req.data);
if (req.files !== undefined && req.files !== null) {
const { file, id } = req.files;
const newFile = dataUri(file).content;
cloudinary.uploader.upload(newFile)
.then(result => {
const imageUrl = result.url;
const data = {id : req.body.id, imageUrl };
updateAvatar(data);
return res.status(200).json({ message: 'Success', data: { imageUrl } });
}).catch(err => res.status(400).json({message:'Error', data: { err}}));
} else {
return res.status(400).json({ message: 'Error' });
}
});
And that's all for my back end code. Here is my front end that cient send image to server:
Here is the method that help user can send image to server:
const UserCard = ({ name, userEmail, isVerified, id, updateUserAvatar, currentUser }) => {
const [selectedValue, setSelectedValue] = useState("a");
const handleChange = (event) => {
setSelectedValue(event.target.value);
};
const [imageSelected, setImageSelected] = useState("");
const uploadImage = () => {
const formData = new FormData();
formData.append("file", imageSelected);
formData.append("id", id);
axios
.post("/api/v1/profiles/upload/image", formData, {
headers: { "Content-Type": "multipart/form-data" },
})
.then((response) => {
updateUserAvatar(response.data.data.imageUrl);
});
};
useEffect(() => {
if (imageSelected !== '') {
uploadImage();
}
}, [imageSelected]);
return (
<div className="avatar--icon_profile">
<Card className="profile--card_container">
<CardContent>
{currentUser.picture ? (
<div>
<input
className="my_file"
type="file"
ref={inputFile}
onChange={(e) => setImageSelected(e.target.files[0])}
/>
<div className="profile-image">
<Avatar
src={currentUser.picture}
alt="Avatar"
className="avatar--profile_image"
onClick={onButtonClick}
/>
</div>
</div>
and here is my Global State. I tried to update nested state in my context but seems like it didn't work.
const GlobalState = (props) => {
// User State -----------------------------------------------------------------------------
const [currentUser, setUser] = useState(props.serverUserData);
console.log(currentUser)
const updateUser = (userData) => {
setUser(userData);
};
// This method is passed through context to update currentUser Avatar
const updateUserAvatar = (picture) => {
setUser({ ...currentUser, picture: picture });
};
const providerValues = {
currentUser,
updateUser,
updateUserAvatar,
};
return (
<GlobalContext.Provider value={providerValues}>
{props.children}
</GlobalContext.Provider>
);
};
export default GlobalState;
and here is my console.log(currentUser) gave me:
{id: "a19cac5c-ea25-4c9c-b1d9-5d6e464869ed", name: "Nhan Nguyen", email: "nhan13574#gmail.com", publicId: "Nh1615314435848", picture: "http://res.cloudinary.com/teammateme/image/upload/v1617229506/gnlooupiekujkrreerxn.png", …}
email: "nhan13574#gmail.com"
id: "a19cac5c-ea25-4c9c-b1d9-5d6e464869ed"
isSessionValid: true
name: "Nhan Nguyen"
picture: "http://res.cloudinary.com/teammateme/image/upload/v1617229506/gnlooupiekujkrreerxn.png"
publicId: "Nh1615314435848"
__proto__: Object
Can anyone help me solve this problem? I really appreciate it
Added GlobalContext.js:
import React from "react";
const globalStateDefaults = {
modals: {
isAuthModalOpen: false,
modalToDisplay: "signup",
toggleModal: () => {},
setModalToDisplay: () => { },
},
user: undefined,
pageName: undefined,
loading: false,
teamProfileId: "",
userProfileId: "",
};
export const GlobalContext = React.createContext(globalStateDefaults);
You need to consume the context where you are trying to update user state.
const {currentUser, updateUser, updateUserAvatar} = React.useContext(GlobalContext)
Then you can call
updateUserAvatar(response.data.data.imageUrl)

NodeJS Axios request returning an odd string

I'm sending a GET request to a third party API and it is returning a odd string (supposed to be an image).
axios.get(`${URL}/test`, {
headers: {
'Content-Type': 'application/json',
},
auth: {
username: USERNAME,
password: PASSWORD
},
responseType: 'blob'
})
.then(async (response) => {
console.log(response.data)
return res.json(response.data);
})
.catch((err) => {
console.log(err);
return res.json("ERROR");
});
The response is:
"����\u0000\u0010JFIF\u0000\u0001\u0001\u0000\u0000\u0001\u0000\u0001\u0000\u0000��\u0000C
How can I convert it to an image or image/url?
Thanks
You can try this
const Fs = require('fs')
const Path = require('path')
const Axios = require('axios')
async function downloadImage () {
const url = 'https://unsplash.com/photos/AaEQmoufHLk/download?force=true'
const path = Path.resolve(__dirname, 'images', 'code.jpg')
const writer = Fs.createWriteStream(path)
const response = await Axios({
url,
method: 'GET',
responseType: 'stream'
})
response.data.pipe(writer)
return new Promise((resolve, reject) => {
writer.on('finish', () => { /* Add your code here */ resolve(); })
writer.on('error', () => { /* Add your code here */ reject(); })
})
}
downloadImage()

Postman can upload file to a Node api. But Not with React

Redux Code
export const uploadImage = data => async dispatch => {
const config = {
headers: {
"Content-Type": "multipart/form-data"
}
};
try {
const res = await axios.post("/api/profile/upload", data, config);
// const res = await axios.post("/api/profile/upload", data, {
// headers: {
// "Content-Type": "multipart/form-data"
// }
// });
console.log(res);
dispatch({
type: AVATAR_UPLOAD,
payload: res.data
});
} catch (err) {
const errors = err.response.data.errors;
if (errors) {
errors.forEach(error => dispatch(setAlert(error.msg, "danger")));
}
dispatch({
type: PROFILE_ERROR,
payload: { msg: err.response.statusText, status: err.response.status }
});
}
};
React Code
const data = new FormData();
data.append("file", avatar);
uploadImage(data);
I get bad request message, req.files is empty
I try to upload file with Postman same configuration,it seems api works but can't upload with react formdata.
Any idea would be appreciated. Thanks in advance
try doing this:
const formData = new FormData();
formData.append('file', file);
const res = await axios.post("/api/profile/upload", formData, config);

Resources