Unable to upload images with multer - node.js

I'm trying to upload images. It's reaching the backend, but the request body and req.image are coming out empty.
I have the submission:
const handleSubmit = async () => {
try {
const data = createFormData();
console.log(data); // prints the correct request object
const response = await axios.post(
`http://${GATEWAY}:5000/api/uploads/single`,
JSON.stringify(data)
);
alert("Upload success!");
console.log("response.data", response.data);
} catch (err) {
console.log("err caught --> ", err);
}
};
const createFormData = () => {
const data = new FormData();
data.append("title", title); // coming from
data.append("body", body); // react hooks state (useState)
data.append("image", {
height: image.height,
width: image.width,
type: image.type,
uri:
Platform.OS === "android" ? image.uri : image.uri.replace("file:/", "")
});
return data;
};
My endpoint:
const express = require("express");
const multer = require("multer");
const bodyParser = require("body-parser");
express().use(bodyParser.json());
const router = express.Router();
// middleware
const auth = require("../../middleware/auth");
const storage = multer.diskStorage({
destination(req, file, callback) {
callback(null, "./images");
},
filename(req, file, callback) {
callback(null, `${file.fieldname}_${Date.now()}_${file.originalname}`);
}
});
const upload = multer({ storage });
// #route POST api/uploads/single
// #desc Upload single image
// #access Private
router.post(
"/single",
// upload.array("photo", 3),
auth,
upload.single("image"),
(req, res) => {
console.log("req", req.body); // {}
console.log("req", req.image); // undefined
return res.status(200).json({
message: "Response from backend"
});
}
);
module.exports = router;
And my server.js
const express = require("express");
const connectDb = require("./config/db");
const app = express();
// connect to db
connectDb();
// Define routes (some omitted for brevity)
app.use("/api/uploads", require("./routes/api/uploads"));
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Listening on port ${PORT}`));
For some reason, in the first snippet, if I do not stringify the data: FormData object that is built from createFormData(), my backend is never even reached.
I've tried so many things, and I'm not starting to think that maybe my backend isn't setup properly. The line where I'm doing express().use(bodyParser.json()); exists because I can't do app.use(bodyParser.json()); (or at least I think), because the app object is in the main server.js file. I'm including other API routes in other files.
For example, my server.js has these, amongst others:
// Define routes
app.use("/api/auth", require("./routes/api/auth"));
app.use("/api/users", require("./routes/api/users"));
app.use("/api/profile", require("./routes/api/profile"));
app.use("/api/uploads", require("./routes/api/uploads"));
And I was following this tutorial to use multer with react-native. A little lost at this point, not sure what I'm doing wrong.
Edit:
I'm making the request like this now,
const config = {
headers: {
"Content-Type": "multipart/form-data"
}
};
const response = await axios.post(
`http://${GATEWAY}:5000/api/uploads/single`,
data,
config
);
But It's failing with a
If I stringify it, it hits the backend but not in the way I need it to:
Edit:
I got it working by specifying the image type, as per the suggestion here

I think this is because you are not setting your content-type to multipart/form-data. Try adding this to your request options:
const response = await axios.post(
`http://${GATEWAY}:5000/api/uploads/single`,
data,
headers: {
`Content-Type`: `multipart/form-data`
}
);
Because of multipart/form-data, do not stringify the data you are sending. Stringifying the data will cause it to only be read as text by the server but is expecting a file to be attached.

Got it working by specifying the image type as per the suggestion here

You're trying to access the file from req.image and req.body, but as mentioned in the https://www.npmjs.com/package/multer, you can access it from :
req.file
req.body will hold the text fields only, on the other hand, if you only uploaded a single file you can find it in req.file, but if you uploaded multiple files you will find them in req.files.

I used the following line to get images:-
concole.log(req.files);

Related

How do I grab json from an external api (serp api) from my backend and then make that same data available for my front end application?

Right now I have a front end react application using axios and and a backend server using node.js and express. I cannot for the life of me get my serp api data to post so that my front end can get it through axios and display the json data. I know how to get data to the front end but I am not a backend developer so this is proving to be incredibly difficult at the moment. I'm able to get the data from the the external api, I just don't know how to post it once I get it. Also I would not like to have all these request running on server.js so I created a controller but I think that is where it is messing up. Any help is appreciated
//pictures controller
const SerpApi = require('google-search-results-nodejs');
const {json} = require("express");
const search = new SerpApi.GoogleSearch("674d023b72e91fcdf3da14c730387dcbdb611f548e094bfeab2fff5bd86493fe");
const handlePictures = async (req, res) => {
const params = {
q: "Coffee",
location: "Austin, Texas, United States",
hl: "en",
gl: "us",
google_domain: "google.com"
};
const callback = function(data) {
console.log(data);
return res.send(data);
};
// Show result as JSON
search.json(params, callback);
//res.end();
}
// the above code works. how do i then post it to the server so that i can retrieve it to the backend?
module.exports = {handlePictures};
//server.js
const express = require('express');
const app = express();
const path = require('path');
const cors = require('cors');
const corsOptions = require('./config/corsOptions');
const { logger } = require('./middleware/logEvents');
const errorHandler = require('./middleware/errorHandler');
const cookieParser = require('cookie-parser');
const credentials = require('./middleware/credentials');
const PORT = process.env.PORT || 3500;
// custom middleware logger
app.use(logger);
// Handle options credentials check - before CORS!
// and fetch cookies credentials requirement
app.use(credentials);
// Cross Origin Resource Sharing
app.use(cors(corsOptions));
// built-in middleware to handle urlencoded form data
app.use(express.urlencoded({ extended: false }));
// built-in middleware for json
app.use(express.json());
//middleware for cookies
app.use(cookieParser());
//serve static files
app.use('/', express.static(path.join(__dirname, '/public')));
// routes
app.use('/', require('./routes/root'));
app.use('/pictures', require('./routes/api/pictures'));
app.all('*', (req, res) => {
res.status(404);
if (req.accepts('html')) {
res.sendFile(path.join(__dirname, 'views', '404.html'));
} else if (req.accepts('json')) {
res.json({ "error": "404 Not Found" });
} else {
res.type('txt').send("404 Not Found");
}
});
app.use(errorHandler);
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
//api/pictures.js
const picturesController= require('../../controllers/picturesController');
const express = require('express')
const router = express.Router();
// for POST request use app.post
router.route('/')
.post( async (req, res) => {
// use the controller to request external API
const response = await picturesController.handlePictures()
// send the response back to client
res.json(response)
})
module.exports = router;
You just need to return the result from SerpApi in your handlePictures function. To do this make a new Promise and when search.json runs callback do what you need with the results and pass it in resolve.
Your picturesController.js with an example of returning all results.
//pictures controller
const SerpApi = require("google-search-results-nodejs");
const { json } = require("express");
const search = new SerpApi.GoogleSearch(process.env.API_KEY); //your API key from serpapi.com
const handlePictures = async (req, res) => {
return new Promise((resolve) => {
const params = {
q: "Coffee",
location: "Austin, Texas, United States",
hl: "en",
gl: "us",
google_domain: "google.com",
};
const callback = function(data) {
resolve(data);
};
search.json(params, callback);
});
};
module.exports = { handlePictures };
Output:
And I advise you to change your API key to SerpApi to prevent it from being used by outsiders.
Since I don't have the full context of your App I can just assume the context. But given the fact that you already have wrapped the logic of calling the external API into a dedicated controller you can use it in the following way in an express app (used the hello world example from express):
// import your controller here
const express = require('express')
const app = express()
const port = 3000
// for POST request use app.post
app.get('/', async (req, res) => {
// use the controller to request external API
const response = await yourController.method()
// send the response back to client
res.json(response)
})
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})
Here's an example how to execute the http request from the frontend:
const response = await fetch('http://localhost:3000') // result from res.json(response)

Downloading JSON file in expressjs and reading it

I have a task where I am given a URL such as https://xyz.json. This URL prompts the downloading of the JSON file into the local. I am now required to read the use this JSON data for further processing. Since I am new to NodeJS and express, I find myself confused about how to achieve this in ExpressJS.
This is what I've tried :
const https = require("https");
const fs = require("fs");
const file = fs.createWriteStream("outputFile.json");
const request = https.get(
"https://xyz.json",
function (response) {
response.pipe(file);
// after download completed close filestream
file.on("finish", () => {
file.close();
console.log("Download Completed");
});
}
);
Here, in the outputFile.json, no data is present
Qn2) Can I periodically download using setTimeOut(). Would it be efficient or is there any better way of caching data to make the application faster?
Thanks in advance!
Here's a sample app that downloads a json triggered when you hit an API route hosted as ExpressJS sever.
const express = require('express');
const cors = require('cors');
const morgan = require('morgan');
const bodyParser = require('body-parser');
const axios = require('axios');
const fs = require('fs');
const app = express();
app.use(cors());
app.use(morgan(':method :url :status :user-agent - :response-time ms'));
app.use(bodyParser.json());
app.get('/', async (req, res) => {
try {
const { status, data } = await axios.get('http://52.87.135.24/json-files/events.json'); // Can be replaced by your json url
if (status === 200) {
fs.writeFileSync('data.json', JSON.stringify(data));
res.status(200).json({
success: 'Downloaded file.',
data: data // Comment it if you don't want to send the data back
})
} else {
res.status(404).json({ 'Failed': 'File not found.' })
}
} catch (err) {
console.log(err);
res.status(500).json({ 'Error': 'Internal Server Error' });
}
});
app.listen(process.env.PORT || 3000, function () {
console.log('Express app running on port ' + (process.env.PORT || 3000))
});
And as I mentioned that this download gets triggered every time you make a request on http://localhost:3000 in this case, you can create a client script that acts like a cron job in which you can use the setTimeout or actually, setInterval to download your file periodically.
const axios = require('axios');
setInterval(async () => {
await axios.get('http://localhost:3000/');
}, 5000);
Here's such a script along! :)

Why is server getting empty request.body from POST http request made with axios?

I'm trying to send a file (.obj file) via formdata to my node server, it appears that everything is fine until the controller of the endpoint on the server throws an error that seems to be like the formdata parser is receiving an empty formdata object, here is the error in question. Now, here's my code:
Fron-end (where the request is being executed):
const data = new FormData();
data.append('file', this.props.filesSubmit, this.props.filesSubmit.name);
for (var pair of data.entries()) {
console.log(pair[0] + ', ' + pair[1]);
}
await axios
.post(
'https://hushpuppys-3d-hub-api.herokuapp.com/api/v1/modelfiles/',
data,
{
headers: {
'Content-Type': undefined,
},
}
)
.then((res) => {
console.log('File Upload Successful! Res: ', res);
})
.catch((err) => {
console.log(err);
});
Back-end endpoint Controller (Where request is received):
const AWS = require('aws-sdk');
const fs = require('fs');
const fileType = require('file-type');
const bluebird = require('bluebird');
const multiparty = require('multiparty');
// Keys Configuration to Access AWS
AWS.config.update({
accessKeyId: '[I erased this]',
secretAccessKey: '[I erased this]',
});
// Configuring AWS to work with promises
AWS.config.setPromisesDependency(bluebird);
// Creating S3 instance
const s3 = new AWS.S3();
// abstracts function to upload a file returning a promise
const uploadFile = (buffer, name, type) => {
const params = {
ACL: 'public-read',
Body: buffer,
Bucket: '[I erased this]',
ContentType: type.mime,
Key: `${name}.${type.ext}`,
};
return s3.upload(params).promise();
};
exports.fileUploaderController = (req, res) => {
const form = new multiparty.Form();
form.parse(req.body, async (error, fields, files) => {
if (error) throw new Error(error);
try {
const path = files.file[0].path;
const buffer = fs.readFileSync(path);
const type = fileType(buffer);
const timestamp = Date.now().toString();
const fileName = `bucketFolder/${timestamp}-lg`;
const data = await uploadFile(buffer, fileName, type);
return res.status(200).send(data);
} catch (error) {
return res.status(400).send(error);
}
});
};
I want to also add the code of my app.js where other middlewares manipulate the body, maybe that's also relevant to solve the problem:
const express = require('express');
const modelRouter = require('./routes/modelRoutes');
const modelFilesRouter = require('./routes/modelFilesRoutes');
const cors = require('cors');
const app = express();
// MIDDLEWARES
app.use(cors());
app.use(express.json({ limit: '50mb' }));
app.use(express.urlencoded({ limit: '50mb' }));
// ROUTES
app.use('/api/v1/models', modelRouter);
app.use('/api/v1/modelfiles', modelFilesRouter);
module.exports = app;
Ok, I got this problem solved a few minutes ago. I was basically passing a wrong parameter in the form.parse() method in the fileUploaderController controller from the Controller file, the method form.parse() needs the whole req variable to be passed as a parameter, not the body of the request (req.body) as I did in the code I posted with the question.

req.body return empty object when i send my formdata in express.js

My problem is a bit tricky one. I know everyone will say bodyparser for solution but i used bodyparser as well but I get still same error.
this is my back end server.js
const fs = require('fs')
const path = require('path')
const express = require('express') // We import express module in our project.
const app = express() // We assign express's functions to app variable.
const bodyParser = require('body-parser') // BodyParser catches data from the http request(POST,PATCH,PUT) and parsing this data to JSON object.
const HttpError =require('./models/HttpError')
const mongoose = require('mongoose')
const userRouter = require('./routes/user-routes')
const recipeRouter = require('./routes/recipe-routes')
const mealPlanRouter = require('./routes/mealplan-routes')
app.use(bodyParser.json())
app.use(
bodyParser.urlencoded({
extended: true
})
);
// We catch data from request and turn this data to json object with BodyParser.
app.use('/uploads/images', express.static(path.join('uploads','images'))) // We create middleware for uploading images and we called this middleware here.
app.use((req, res, next) => { // We need to write this middleware. Because We decide to how to get a request from the client.This is like protocol between server and client for the communication.
res.setHeader('Access-Control-Allow-Origin','*')
res.setHeader('Access-Control-Allow-Headers',
'Origin, X-Request-With, Content-Type, Accept, Authorization'
)
res.setHeader('Access-Control-Allow-Methods','GET, POST, PUT, DELETE')
next()
})
app.use('/api/users',userRouter)
app.use('/api/recipes',recipeRouter)
app.use('/api/mealplans',mealPlanRouter)
app.use((req, res, next) => { // When the client try to access wrong routes. We need to say the client is going wrong way.
const error = new HttpError('Could not find this route', 404)
throw error
})
app.use((err,req,res,next) => { // We check if user pick file for importing an image.
if(req.file){
fs.unlink(req.file.path, err => {
console.log(err)
})
}
if(res.headerSent){
return next(err)
}
res.status(err.code || 500)
res.json({message:err.message || 'Unknown error occured'})
})
// We connect our project with database. Mongoose communicate with database and we communicate with mongoose. This way is more secure.
mongoose
.connect(`mongodb+srv://${process.env.DB_USER}:${process.env.DB_PASSWORD}#cluster0-vvtq0.mongodb.net/${process.env.DB_NAME}?retryWrites=true&w=majority`,{ useNewUrlParser: true, useUnifiedTopology: true})
.then(() => {
app.listen( process.env.PORT || 5000, () => {
console.log('The Server is running')
})
})
.catch(err => {
console.log(err)
})
this is where i send formdata. I check my formdata from network tab. It seems fine. when i check my formdata with req.body in my server.js returns empty object.
const handleSubmit = async e => {
e.preventDefault()
const datestr = (new Date(startDate)).toUTCString();
try{
const formData = new FormData()
formData.append('title',formState.inputs.title.value)
formData.append('date',datestr)
formData.append('timeFrame',formState.inputs.timeFrame.value)
formData.append('targetCalories',formState.inputs.targetCalories.value)
formData.append('diet',formState.inputs.diet.value)
formData.append('exclude',excludeData.join())
formData.append('creator',auth.userId)
const responseData = await axios.post(
process.env.REACT_APP_BACKEND_URL+'/mealplans/new',
formData,{
headers: {Authorization : `Bearer ${auth.token}`} })
console.log(responseData)
}
catch(err){
console.log(err.message)
}
}
Thanks from now for your helps.
FormData objects get converted to Multi-part request bodies.
You need a body parser capable of handling that data format. You only have body parsers for JSON and URL encoded data.
The body-parser module homepage recommends 4 modules which support Multi-part bodies.

how to read posted data on node js api

i want to read the csv data uploaded to backened.
for this i am sending the data via post from front end..
frontend code:
fileEvent(e) {
this.filedata = e.target.files;
if (this.filedata.length > 0) {
const file: File = this.filedata[0];
console.log(file);
const formData: FormData = new FormData();
formData.append('files', file, file.name);
this.http.post('myUrl', {file: formData}, this.options)
.subscribe((res) => {
});
}
}
screenshot of my file:
now on backened i have written route on api.js that directs me
to the controller i have created.
my api.js code:
router.post('/product/csvdata', function (req, res) {
productimport.importcsvProduct(req, res);
});
and finally on my controller i am consoling my data:
var product = {
importcsvProduct: function (req,res) {
console.log(req.body.file);
}
};
module.exports = product;
but i am getting empty {} in console..??
can anyone check whats wrong with this..??
You need to use a file handling middleware in this case, such as multer.
const express = require('express')
const multer = require('multer')
const upload = multer({ dest: 'uploads/' })
const app = express()
app.post('/profile', upload.single('csvdata'), function (req, res, next) {
// req.file is the `csvdata` file
// req.body will hold the text fields, if there were any
})

Resources