I am getting the above mentioned error on uploading more than one image. For one image it's working well but multiple images it's creating a problem. It shows validation error because on append two time images its also appending the name and lastname two times.
In the Front-end, I am using React and on the backend, I am using the Nodejs Express MongoDB and multer for image uploading.
Client#####
constructor(props) {
super(props);
//binding
this.onFileChange = this.onFileChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.handleChange = this.handleChange.bind(this);
this.state = {
imgCollection: '',
name: '',
lastName: ''
}
}
// For the req body data on change on the text input
handleChange(e) {
this.setState({
[e.target.id]: e.target.value
})
}
//for image on change
onFileChange(e) {
this.setState({ imgCollection: e.target.files })
}
//FOr form submition
onSubmit(e) {
e.preventDefault()
var formData = new FormData();
for (const key of Object.keys(this.state.imgCollection)) {
formData.append('imgCollection', this.state.imgCollection[key])
formData.append('name', this.state.name )
formData.append('lastName', this.state.lastName)
}
axios.post("http://localhost:4000/api/upload-images", formData, {
}).then(res => {
console.log(res.data)
})
/* fetch('http://localhost:4000/api/upload-images', { method: 'POST', body: formData })
.then(res => {
res.json()
console.log(res)}) */
}
Server####
router.post('/upload-images', upload.array('imgCollection', 6), (req, res, next) => {
const reqFiles = [];
const url = req.protocol + '://' + req.get('host')
for (var i = 0; i < req.files.length; i++) {
reqFiles.push(url + '/public/' + req.files[i].filename)
}
const user = new User({
_id: new mongoose.Types.ObjectId(),
imgCollection: reqFiles,
...req.body
});
user.save().then(result => {
res.status(201).json({
message: "Done upload!",
userCreated: {
_id: result._id,
imgCollection: result.imgCollection,
name: result.name,
lastName: result.lastName
}
})
}).catch(err => {
console.log(err),
res.status(500).json({
error: err
});
})
})
I noticed that you are appending formData multiple times using the same key. I don't think that would work. Generally, while adding multiple values to a field in formData - in order to append them as an array, here's what you need to do, add '[]' at the end of the key name. Here's a function you can try using to add multiple files:
this.state.imgCollection.forEach(image => formData.append('imgCollection[]', image))
Related
This is the code for on my front end
When the object vendor is sent to the backend, the array key, value pair businessImages is empty [I came to this conclusion when nothing got console logged when I tried logging businessImages in the backend]
When I console log the vendor object, from the front end, it clearly shows that businessImages is populated. I have no idea what's going on.
Frontend code (reactjs)
const handleSubmit = async (e) => {
e.preventDefault();
if (businessImages.length > 15) {
alert("You've selected too many images for your business");
setBusinessImages([]);
} else {
const formDataBusinessImages = new FormData();
const formDataProfilePicture = new FormData();
vendor = {
name: name.current.value,
email: email.current.value,
mobileNumber: mobileNumber.current.value,
businessAddress: businessAddress.current.value,
mainService: selectedService,
subServive: selectedSubService,
businessImages: [],
aboutBusiness: aboutBusiness.current.value,
minPrice: minPrice.current.value,
maxPrice: maxPrice.current.value,
featured: featured,
};
formDataProfilePicture.append("file", profilePicture);
formDataProfilePicture.append(
"upload_preset",
process.env.REACT_APP_CLOUDINARY_KEY
);
try {
await axios
.post(
"https://api.cloudinary.com/v1_1/devign/image/upload",
formDataProfilePicture
)
.then((res) => {
console.log("Profile pic uploaded");
vendor.profilePicture = res.data.secure_url;
});
} catch (err) {
console.log(err);
}
businessImages.map(async (image) => {
formDataBusinessImages.append("file", image);
formDataBusinessImages.append("upload_preset", process.env.REACT_APP_CLOUDINARY_KEY);
try {
await axios
.post(
"https://api.cloudinary.com/v1_1/devign/image/upload",
formDataBusinessImages
)
.then((res) => {
vendor.businessImages.push(res.data.secure_url);
});
} catch (err) {
console.log(err);
}
});
handleUpload(vendor);
}
};
const handleUpload = async (uploadData) => {
try {
// console.log(businessImagesUrl);
// console.log(uploadData);
await axios.post("/auth/register", uploadData);
} catch (err) {
console.log(err);
}
};
Backend code (nodejs)
router.post("/register", async (req, res) => {
const newVendor = new Vendor({
name: req.body.name,
email: req.body.email,
mobileNumber: req.body.mobileNumber,
businessAddress: req.body.businessAddress,
mainService: req.body.mainService,
subService: req.body.subService,
profilePicture: req.body.profilePicture,
businessImages: [],
minPrice: req.body.minPrice,
maxPrice: req.body.maxPrice,
featured: req.body.featured,
});
try {
newVendor.businessImages.push(req.body.businessImages);
console.log(req.body.businessImages, req.body.profilePicture);
const vendor = await newVendor.save();
res.status(200).json("Successfully registered");
} catch (err) {
console.log(err);
}
});
I am new to websockets and socket.io. I have a few challenges>
I have developed the application with react and nodejs using socket.io.
useEffect(() => {
const getAllChats = async (token) => {
try {
setLoading(true)
const res = await getChats(token)
if (res) {
setChats(res)
setLoading(false)
}
} catch (err) {
console.log(err)
setLoading(false)
}
}
getAllChats(user.token)
}, [])
export const getChats = (token) => {
return request(`/api/v1/chats`, {
token,
method: 'GET',
})
}
The above code is used to fetch the chat from the backend.
At the backend, I have the code:
exports.getAllChats = catchAsync(async (req, res, next) => {
const user = await Chat.findOne({ user: req.user._id }).populate(
'chats.messagesWith'
)
let chatsToBeSent = []
if (user.chats.length > 0) {
chatsToBeSent = await user.chats.map((chat) => ({
messagesWith: chat.messagesWith._id,
firstName: chat.messagesWith.firstName,
lastName: chat.messagesWith.lastName,
profilePicUrl: chat.messagesWith.profilePicUrl,
lastMessage: chat.messages[chat.messages.length - 1].msg,
date: chat.messages[chat.messages.length - 1].date,
}))
}
return res.json(chatsToBeSent)
})
How can I make new messages remain at the top of the list? Presently, It is always ordered by created date
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)
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 },
});
}
};
I am trying to create a collection in mongodb where a field named lists will contain an array of link and linkName. I am successfully able to create a two seperate field link and linkName, however not able to store the value inside lists.
Model code for mongodb :-
const socialSchema = new Schema({
lists: [{
link:{ formType: String},
linkName: { formType: String}
}]
})
API code :-(this code is for creating only, will later on try to use findOneAndUpdate to update the existing field
router.route('/', [auth]).post(async (req, res) => {
const {linkName, link } = req.body
try {
console.log(req.body)//Ex. { linkName: 'facebook', link: 'www.facebook.com'}
const social = new Social({
//Stuck here!!!
})
await social.save()
res.json(social)
} catch (err) {
console.error(err.message);
res.status(500).send('Server Errors')
}
}
)
Part of frontend Code(React)
const [formData, setFormData] = useState({
linkName: '',
link: ''
});
const {linkName, link} = formData
const onChange = e =>
setFormData({ ...formData, [e.target.name]: e.target.value });
const handleSubmit = async e => {
e.preventDefault()
const socialList = {
linkName,
link
}
try {
const config = {
headers: {
'Content-Type': 'application/json'
}
};
const body = JSON.stringify(socialList)
const res = await Axios.post('/api/social', body, config)
console.log(res)
} catch (err) {
console.error(err);
}
}
In your schema change from {formType: String} to {type: String}.
const data = {link: req.body.link, linkName: req.body.linkName};
Social.create({
links: [data]
});
This should work.
MY FULL WORKING CODE THAT I TESTED
const schema = new mongoose.Schema({
links: [
{
link: { type: String },
linkName: { type: String }
}
]
});
const Model = mongoose.model("test", schema);
const doc = { link: "link", linkName: "linkname" };
Model.create({
links: [doc]
});