Sending a HTTP POST REQUEST with image and text - javascript

How can I send an image along with a text in VueJs to my backend ExpressJs?
Right now, what I did was create two http post request
NOTE this.albumName and this.albumDesc are just text and the formData is an image.
createAlbum() {
const formData = new FormData();
for (let file of Array.from(this.myAlbumImages)) {
formData.append("files", file);
}
if (this.albumName) {
axios
.post("http://localhost:9001/image/album", {
ALBUM: this.albumName,
DESCRIPTION: this.albumDesc
})
.then(resp => console.log(resp))
.catch(err => console.log(err));
setTimeout(function() {
axios
.post("http://localhost:9001/image/album", formData)
.then(resp => console.log(resp))
.catch(err => console.log(err));
}, 3000);
this.albumName = "";
this.albumDesc = "";
} else {
alert("Please fill the above form.");
}
},
and here is my Backend.
This creates the folder based on the passed data and it also creates a named undefined folder
router.post('/album', (req, res) => {
let sql = "INSERT INTO GALLERY SET ALBUM = ?, DESCRIPTION = ?";
let body = [req.body.ALBUM, req.body.DESCRIPTION]
myDB.query(sql, body, (error, results) => {
if (error) {
console.log(error);
} else {
let directory = `C:/Users/user/Desktop/project/adminbackend/public/${req.body.ALBUM}`;
fse.mkdirp(directory, err => {
if (err) {
console.log(err);
} else {
console.log(directory);
}
})
}
})
I think this is because of NodeJS is Asynchronous that's why it creates the undefined folder.

Reason for behavior you see is you are sending two different requests to the same route. 1st includes ALBUM and DESCRIPTION form field values, but not the files. Second (inside setTimeout) will contain just files and no other fields, so referencing them like req.body.ALBUM will return undefined
You can send all data (text fields and files) in one request. Just do this:
const formData = new FormData();
for (let file of Array.from(this.myAlbumImages)) {
formData.append("files", file);
}
formData.append("ALBUM", this.albumName);
formData.append("DESCRIPTION", this.albumDesc);
axios.post("http://localhost:9001/image/album", formData)
.then(resp => console.log(resp))
.catch(err => console.log(err));
FormData always uses content type multipart/form-data. To parse it on server side you need Express middleware that parses multipart forms, and gives you access to both fields and image/s. For example multer...

for the first part the client may can help you this link How to post image with fetch?
const fileInput = document.querySelector('#your-file-input') ;
const formData = new FormData();
formData.append('file', fileInput.files[0]);
const options = {
method: 'POST',
body: formData,
// If you add this, upload won't work
// headers: {
// 'Content-Type': 'multipart/form-data',
// }
};
fetch('your-upload-url', options);
and for the seerver part cant help you this link
Node Express sending image files as API response
app.get('/report/:chart_id/:user_id', function (req, res) {
res.sendFile(filepath);
});
and oficial documentation about this
http://expressjs.com/en/api.html#res.sendFile

Related

Why files are not being uploaded in the back-end from react-front-end

Recently I started working on a project where I am using react as my front-end and node as my back-end. In this project, I have a requirement to upload multiple images from the front-end and my back-end will receive the files and they will be uploaded in cloudinary. I have created an API route for that. Which works just fine! but when I am trying to do that same thing from my react front-end, I am not receiving any files just getting an empty [] but when I was using postman, I was receiving all the files properly inside an array. What is the issue here? I guess this is a problem from my front-end. But I can't figure out what is the problem.
I am sharing my front-end and back-end code:-
front-end
const Upload = () => {
const [fileAra, setFileAra] = useState([]);
// for sending these files to the back-end
async function uploadProduct() {
try {
const formData = new FormData();
formData.append("assets", fileAra);
const res = await fetch("/get_product/post_product", {
method: "POST",
body: formData,
});
const body = await res.json();
if (res.status === 200) {
history.push(`/profile/${user._id}`);
toast.dark(body.message);
}
} catch (err) {
console.log(err);
}
}
return (
<input type="file" multiple onChange={(e) => setFileAra(e.target.files)} />
<button onClick={uploadProduct}></button>
)
}
Back-end route
router.post(
"/post_product",
checkAuth,
multer.array("assets", 10),
async function(req, res, next) {
try {
const pictureFiles = req.files;
// map through images and create a promise array using cloudinary upload function
const multiplePicturePromise = pictureFiles.map((picture) =>
cloudinary.uploader.upload(picture.path, {
folder: `devR-Commerce/products/${req.user.name}`,
})
);
const imageResponses = await Promise.all(multiplePicturePromise);
// separating each of the images into objects
const allImagesSeparatedInObject = imageResponses.map((img) => ({
photoId: img.public_id,
photoUrl: img.secure_url,
}));
// creating a new product instance
const newProduct = new Product({
user: req.user._id,
images: allImagesSeparatedInObject,
});
// saving the product in my database
const savedProduct = await newProduct.save();
// creating relation between these two collections: Product and User
await User.updateOne(
{ _id: req.user._id },
{ $push: { products: savedProduct._id } }
);
res
.status(200)
.json({ message: "Product uploaded successfully", newProduct });
} catch (err) {
next(err);
}
},
);
Note: The back-end route works properly I have tested with Postman. But when I try to send the same files from my front-end, I don't receive any files. Might be a problem in the front-end. Please help me if you know the answer. If you need any additional information about this problem, please feel free to ask. Thanks for checking this out
You need to append each file to the FormData one by one and not attach the whole array at once, like this.
async function uploadProduct() {
try {
const formData = new FormData();
for (let i = 0; i < fileAra.length; i++) {
formData.append(fileAra[i].name, fileAra[i])
}
const res = await fetch("/get_product/post_product", {
method: "POST",
body: formData,
});
const body = await res.json();
if (res.status === 200) {
history.push(`/profile/${user._id}`);
toast.dark(body.message);
}
} catch (err) {
console.log(err);
}
}
you can check out this article explaining FormData for further details

how to send zip file as body in a post request in react?

Let's say I have a zip file uploaded by user, how do I pass it to the server for process?
I have front end function to handle zip file. I can verify that fileUploaded holds the zip file.
const onImport = async evt => {
const fileUploaded = evt.target.files[0];
const response = await extractImport('/extractimport', fileUploaded);
};
Helper function used above
export const extractImport = async (url, fileUploaded) => {
return await fetch(url, {
method: 'POST',
headers: {'accept': 'application/zip'},
body: fileUploaded
});
};
router for post call.
And here, req body is empty. I don't know what went wrong here.
router.post('/extractimport',
asyncWrapper(async (req, res) => {
//req.body is empty here, I dont understand why.
try {
const result = await extractImportPost(req.body);
res.status(200).json(result).end();
} catch (err) {
res.status(500).json({errors: [{error: err.message}]}).end();
}
})
);
extractImportPost utility used above is as below
const extractImportPost= async (zipFile) => {
// zipFile is undefined here.
// how do I pass the zip file to this place for process
}
Thank you for your time.

Uploading Image to Cloudinary Express.js React Axios post request

I am trying to upload images to cloudinary using react front-end and express server.
The problem is i cant properly post request image to my express server.
This is how i prepare image to send it later:
var data = new FormData();
console.log(event.target.files[0]) // this prints FileObject succesfully
data.append('image', event.target.files[0]);
console.log(data) // this prints {} but i guess its natural since its FormData ??
this.imageToUpload = data;
This is how i post request:
axios.post('/api/courses/uploadImage',this.imageToUpload, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
.then( (response) => {
alert(JSON.stringify(response));
})
.catch(function (error) {
console.log(error);
});
Now in server side,req.body is empty.
router.post("/courses/uploadImage",(req,res,next)=>{
console.log(req.body) // empty
var image = req.body;
cloudinary.uploader.upload(image, function(error, result) { console.log(result) });
})
Also what should i really put to first parameter of(image in this case) uploader.upload ?
Not a direct answer but if you want Cloudinary also offers a way to directly upload images from your front-end so it saves you some work. You can read here further.
I've worked with their widget and it is very simple to integrate into almost any app.
You can do like this. I have successfully tried in my project.
function upload(){
var data = new FormData();
data.append('image', event.target.files[0]);
data.append('username', 'Saurabh'); //if you have other fields
axios.post('/api/courses/uploadImage', data,
headers: {
//your headers
})
.then( (response) => {
alert(JSON.stringify(response));
})
.catch(function (error) {
console.log(error);
});
}
In your express route you can simply get values like this
router.post('/api/courses/uploadImage', upload.single('image'), async(req,res, next) => {
const result = await cloudinary.v2.uploader.upload(req.file.path);
console.log(req.body.username); //Saurabh
//your logic
});
I solved my problem by directly uploading images to cloudinary from client with their html5 codepen example.

Cannot upload multiple files

I have a small part of my app that allow users to upload 5 different images and as soon as an image has been uploaded it show a small thumbnail of it.
Since I upload a single image everything works fine.
As soon as I try to upload multiple image, let's sat 3 all together, it end up to upload 3 equal images showing 3 equal thumbnails... And I'm getting crazy.
Here is the code that handle this part:
HTML:
<input id="pics" type="file" accept="image/*" class="upload" data-imageid="" data-imageurl="" data-callback="displayPics" data-image-type="pics" multiple />
JS:
const fileInputs = document.querySelectorAll('input[type="file"]');
[].slice.call(fileInputs).forEach(fileInput => {
fileInput.addEventListener('change', () => {
uploadFile(
fileInput,
fileInput.getAttribute('data-callback')
);
});
});
unction uploadFile(input, callback) {
let formData = new FormData();
Object.keys(input.files).forEach(current => {
formData.append('file', input.files[current]);
fetchAPI({
target: 'pics/',
method: 'POST',
body: formData
})
.then(data => {
if (callback !== undefined) {
window[callback](data.response);
}
}, err => {
// err...
});
});
}
Can I know what is wrong here? Thanks
fetch() call does not have a URL set. "target" is not a valid options property of object passed to fetch().
You also do not call Response.json() to get a JavaScript object representation of Response.
The code at Question makes a request for each File object instead of a single request.
function uploadFile(input, callback) {
let formData = new FormData();
for (let file of input.files) {
// or set property to `file[N]`
// "file["+ Array.from(formData.keys()).length +"]"
formData.append("file", file)
}
fetchAPI('pics/', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (callback !== undefined) {
window[callback](data.response);
}
}, err => {
// err...
})
}

Send a file from mobile to Node js server

I'm doing an application with react-native. Now I'm trying to send an image from the mobile to the server (Node Js). For this I'm using react-native-image-picker. And the problem is that when I send the image it save a file but it's empty not contain the photo. I think that the problem probably is that the server can't access to the path of the image because is in a different device. But I don't know how I can do it.
React-Native:
openImagePicker(){
const options = {
title: 'Select Avatar',
storageOptions: {
skipBackup: true,
path: 'images'
}
}
ImagePicker.showImagePicker(options, (imagen) =>{
if (imagen.didCancel) {
console.log('User cancelled image picker');
}
else if (imagen.error) {
console.log('ImagePicker Error: ', imagen.error);
}
else if (imagen.customButton) {
console.log('User tapped custom button: ', imagen.customButton);
}
else {
let formdata = new FormData();
formdata.append("file[name]", imagen.fileName);
formdata.append("file[path]", imagen.path);
formdata.append("file[type]", imagen.type);
fetch('http://X/user/photo/58e137dd5d45090d0b000006', {
method: 'PUT',
headers: {
'Content-Type': 'multipart/form-data'
},
body: formdata
})
.then(response => {
console.log("ok");
})
.catch(function(err) {
console.log(err);
})
}})}
Node Js:
addPhotoUser = function (req, res) {
User.findById(req.params.id, function(err, user) {
fs.readFile(req.body.file.path, function (err, data) {
var pwd = 'home/ubuntu/.../';
var newPath = pwd + req.body.file.name;
fs.writeFile(newPath, data, function (err) {
imageUrl: URL + req.body.file.name;
user.save(function(err) {
if(!err) {
console.log('Updated');
} else {
console.log('ERROR: ' + err);
}
res.send(user);
});
});
});
});
};
Yes, the problem is that the filepath is on the local device and not the server. You want to send the actual data returned to you by react-native-image-picker not the uri. It looks like that library encodes the data with base64 so you're going to want send that to your server, not the uri returned from the library because it won't be accessible on a remote server.
What this means is that you won't be reading any files on your server but instead just decoding a base64 string in the response body and writing that to your filesystem.
For the client side:
let formdata = new FormData();
formdata.append("file[name]", imagen.fileName);
formdata.append("file[data]", imagen.data); // this is base64 encoded!
formdata.append("file[type]", imagen.type);
fetch('http://X/user/photo/58e137dd5d45090d0b000006', {
method: 'PUT',
headers: {
'Content-Type': 'multipart/form-data'
},
body: formdata
})
On the server side atob to decode from base64 before writing to the filesystem:
let decoded = atob(req.body.data)
// now this is binary and can written to the filesystem
From there:
fs.writeFile(newPath, decoded, function (err) {
imageUrl: newPath;
user.save(function(err) {
if(!err) {
console.log('Updated');
} else {
console.log('ERROR: ' + err);
}
res.send(user);
});
});
Note, you don't need the filesystem write that's in your code because you're decoding the image that was sent as a b64 string in your request.
There also seems to be some oddities with how you're using that user object. You seem to be only passing a function that handles errors and not any actual data. I don't know what ORM you're using so it's hard to say how it should work. Maybe something like this?
user.save({imageUrl:uriReturnedByFsWrite}, (err, data)=>{...})
Good luck :)
Make an object then send that object to the server. The object will consist of name,path and type, like this:
var imageData = {name: 'image1', path: uri, type: 'image/jpeg'}
Above is a one way to send the image data. The other way is to convert it into BLOB so that server side programmer doesn't have to do this task on their end. You can make BLOB by use of react-native-fetch-blob.
One more way is to directly upload the images to the amazon server(s3) and send the link to the backend..
Function that returns base64 string:
var RNFetchBlob = require('react-native-fetch-blob').default;
getImageAttachment: function(uri_attachment, mimetype_attachment) {
return new Promise((RESOLVE, REJECT) => {
// Fetch attachment
RNFetchBlob.fetch('GET', config.apiRoot+'/app/'+uri_attachment)
.then((response) => {
let base64Str = response.data;
var imageBase64 = 'data:'+mimetype_attachment+';base64,'+base64Str;
// Return base64 image
RESOLVE(imageBase64)
})
}).catch((error) => {
// error handling
console.log("Error: ", error)
});
},
Cheers :)

Categories

Resources