Cannot get file uploaded from react js frontend to express backend - javascript

//This is frontend
import React, { useState } from "react";
import axios from "axios";
import "./App.css";
const App = () => {
const [logFile, setLogFile] = useState<File | null>(null);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setLogFile(e.target.files![0]);
};
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
try {
const formData = new FormData();
formData.append("logFile", logFile!);
console.log(formData);
const response = await axios.post(
"http://localhost:3001/logs",
formData,
{
headers: {
"Content-Type": "multipart/form-data",
},
}
);
console.log(response.data);
} catch (error) {
console.error(error);
}
};
return (
<>
<div className="App">
<h1>Upload file</h1>
<form onSubmit={handleSubmit}>
<div>
<input type="file" onChange={handleChange} />
</div>
<button type="submit">Submit</button>
</form>
</div>
</>
);
};
export default App;
//This is backend
const express = require('express');
const app = express();
const port = 3001;
const winston = require('winston');
const cors = require("cors")
app.use(cors())
const logger = winston.createLogger({
transports: [
new winston.transports.File({
level: 'error',
filename: 'error.log',
handleExceptions: true,
json: true,
maxsize: 5242880,
maxFiles: 5,
colorize: false
}),
new winston.transports.File({
level: 'warn',
filename: 'warning.log',
handleExceptions: true,
json: true,
maxsize: 5242880,
maxFiles: 5,
colorize: false
})
],
exitOnError: false
});
app.use(express.json());
app.get("/", (req, res) => {
res.json({ "message": req.body.logFile })
})
app.post('/logs', (req, res) => {
const logFile = req.body.logFile;
const errorLogs = [];
const warnLogs = [];
// Log error and warn messages to separate files
logFile?.forEach(log => {
if (log.level === 'error') {
logger.error(log);
errorLogs.push(log);
} else if (log.level === 'warn') {
logger.warn(log);
warnLogs.push(log);
}
});
res.json({ errorLogs, warnLogs });
});
app.listen(port, () => {
console.log(`Server is listening at http://localhost:${port}`);
});
I'm building a log parser to log error and warn levels using react js to upload file and return filtered data as json from express server. But I'm getting empty object for formData but logfile is showing uploaded fie in console in my react js frontend
I want to send a log file from frontend to backend and return filtered json data from backend to frontend

One issue with your code is that the form element doesn't have an onSubmit event, so the form is never submitted. To fix this, you can wrap your input element and submit button in a form element and add an onSubmit event that calls the handleSubmit function.
Another issue is that you're using the fetch API to make the POST request, which may not work correctly with a multipart/form-data content type. You can use the Axios library instead, which makes it easier to send binary data in the request body.
import React, { useState } from "react";
import axios from "axios";
import "./App.css";
const App = () => {
const [logFile, setLogFile] = useState<File | null>(null);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setLogFile(e.target.files![0]);
};
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
try {
const formData = new FormData();
formData.append("logFile", logFile!);
const response = await axios.post("http://localhost:3001/logs", formData, {
headers: {
"Content-Type": "multipart/form-data",
},
});
console.log(response.data);
} catch (error) {
console.error(error);
}
};
return (
<>
<div className="App">
<h1>Upload file</h1>
<form onSubmit={handleSubmit}>
<div>
<input type="file" onChange={handleChange} />
</div>
<button type="submit">Submit</button>
</form>
</div>
</>
);
};
export default App;
Edit:
The problem is that you are using req.body.logFile to access the uploaded file, but the file is being sent as a multipart/form-data request, not as a JSON request. You need to use a middleware that can handle multipart/form-data requests and extract the file.
To handle multipart/form-data requests in an Express application, you can use the multer middleware. Here is an example of how you can use it to extract the file from the request:
const multer = require('multer');
const storage = multer.memoryStorage();
const upload = multer({ storage });
app.post('/logs', upload.single('logFile'), (req, res) => {
const logFile = req.file;
...
});
In this example, the upload.single('logFile') will extract the file from the logFile field of the multipart/form-data request and store it in memory as a Buffer. You can then access the file using req.file

Related

Axios Post Body Empty with Express

I'm trying to send over two pieces of text data from my React frontend to an Express backend but whenever I do the post command with Axios the body appears as {} in the backend and I cannot use it. My code is listed below.
Client (App.js):
import { useState, useEffect } from 'react';
import React from 'react'
import './App.css';
import Axios from 'axios'
function App() {
const [cocktailName, setCocktailName] = useState("");
const [cocktailMain, setCocktailMain] = useState("");
const submitRecipe = () => {
const recipeData = {"cocktailName": cocktailName, "cocktailMain": cocktailMain};
Axios.post('http://localhost:3001/api/insert', recipeData).then(() => {alert('successful insert');});
}
return (
<div className="App">
<h1>CRUD Application Test</h1>
<div className='InputForm'>
<label> Cocktail Name: </label>
<input type="text" name="Cocktail Name" onChange={(e)=>
{setCocktailName(e.target.value);}}/>
<br></br>
<label> Cocktail Main Ingredient: </label>
<input type="text" name="Cocktail Main Ingredient" onChange={(e)=> {
setCocktailMain(e.target.value)}}/>
<br></br>
<button onClick={submitRecipe}>Submit</button>
</div>
</div>
);
}
export default App;
Server (App.js):
const app = express()
const mysql = require('mysql')
const bodyParser = require('body-parser')
const cors = require('cors')
const db = mysql.createPool({
host: 'localhost',
user: 'root',
password: 'password',
database: 'cruddatabase'
});
app.use(cors());
app.use(bodyParser.urlencoded({extended: true}));
app.post('/api/insert', (req, res)=> {
console.log(req.body)
const cocktailName = req.body.cocktailName;
const cocktailMain = req.body.cocktailMain;
console.log(cocktailName)
console.log(cocktailMain)
//This is where i'll eventually insert it into a database
const sqlInsert = "INSERT INTO cocktail_recipes (cocktailName, cocktailMain) VALUES (?,?)"
// db.query(sqlInsert, [cocktailName, cocktailMain], (err, result) => {
// console.log(err)
// });
});
app.listen(3001, () => {
console.log("running on port 3001")
});
Any help at all is greatly appreciated.
Change this line:
app.use(bodyParser.urlencoded({extended: true}));
With this one:
app.use(express.json());
Axios send a JSON when you give an object as data without specifying the Content-Type like you did. So urlencoded is not the right set here.
you need to have a res.send() somewhere within this block
app.post('/api/insert', (req, res)=> {
console.log(req.body)
const cocktailName = req.body.cocktailName;
const cocktailMain = req.body.cocktailMain;
console.log(cocktailName)
console.log(cocktailMain)
//This is where i'll eventually insert it into a database
const sqlInsert = "INSERT INTO cocktail_recipes (cocktailName, cocktailMain) VALUES (?,?)"
// db.query(sqlInsert, [cocktailName, cocktailMain], (err, result) => {
// console.log(err)
// });
});
Firstly to get a response from your backend, you need to specify what to receive by yourself, you can send the response as json by doing: res.json("..."). You should change the ... to any response you want to get back. And if you want to get your data back, you can put it there. You can also do res.send("...") to send a message after the request was completed
Secondly, you need to let your backend accept json data by adding this after the app variable.
app.use(express.json());
Lastly, I would encourage you to you async function to make your code looks cleaner. You can change your post request code to something like this and let me know if it works.
app.post("/api/insert", async (req, res) => {
try {
const { cocktailName, cocktailMain } = req.body;
const sqlInsert = await "INSERT INTO cocktail_recipes (cocktailName, cocktailMain) VALUES (?,?)";
} catch (e) {
console.log(e.message);
}
});

next.js app works fine in local but returns 500 internal server error on production

the problem i'm having is basically my app works fine in local but in production anywhere that i've used server side rendering returns 500 internal server error. the other parts of my site which are called normally like in useEffect or componentDidMount work completely fine, like my dashboard or authorization process works without a problem, but anywhere that i have used ssr returns 500.
Below is some examples of how i have handled my ssr pages.
index page:
import React from 'react';
import HomePage from '../components/homePage/index'
import { Api, GuestHeaders } from '../components/config'
const Home = (props) => {
return <HomePage {...props} />
}
export async function getServerSideProps() {
const Response = await Api.get(`/v1/index`, { headers: GuestHeaders })
return {
props: {
Detail: Response.data,
}
}
}
export default Home
here is my Api component:
import axios from 'axios';
const GuestHeaders = {
'Authorization': "",
'content-type': 'application/json'
}
const Api = axios.create({
baseURL: 'baseUrl'
})
export { Api, GuestHeaders };
here is my server.js:
// server.js
const { createServer } = require('http')
const { parse } = require('url')
const next = require('next')
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()
app.prepare().then(() => {
createServer((req, res) => {
// Be sure to pass `true` as the second argument to `url.parse`.
// This tells it to parse the query portion of the URL.
const parsedUrl = parse(req.url, true)
const { pathname, query } = parsedUrl
}).listen(3000, (err) => {
if (err) throw err
console.log('> Ready on http://localhost:3000')
})
})
and my next.config.js:
module.exports = {
basePath: '',
trailingSlash: false,
}

issues using base64 with showing image on client side

I am trying to upload an image and show it on the client side using base 64. I am successful in sending to mongodb but not in showing the image once it is uploaded.
My theory: I need to have the setDefaultImage function outside the setGetBaseFile function to fix to be able to see image on client side.
when I attempt to move it I get a "Error: Too many re-renders. React limits the number of renders to prevent an infinite loop. The only way arround this error that I have found is to move the function back in the parent function.
Below is my code for image.js
const mongoose = require("mongoose");
const Image = mongoose.model("gallery");
const express = require('express');
const ImageRouter = express.Router();
const DIR = './public/';
/* upload image in base64 format, thereby, directly storing it in mongodb datanase
along with images uploaded using firebase storage */
ImageRouter.route("/uploadbase")
.post((req, res, next) => {
const newImage = new Image({
imageName: req.body.imageName,
imageData: req.body.imageData
});
newImage.save()
.then((result) => {
res.status(200).json({
success: true,
document: result
});
})
.catch((err) => next(err));
});
module.exports = ImageRouter;
Below is my client side code:
import React, { useState } from "react";
import Container from "react-bootstrap/Container";
import Card from "react-bootstrap/Card";
// import Button from "react-bootstrap/Button";
// import "./postverse.css";
import Form from "react-bootstrap/Form";
import axios from "axios";
import FileBase from 'react-file-base64';
import DefaultImg from '../../assets/default-image.jpg';
const GlobalPost = () => {
const API_URL = "http://localhost:5000";
const [baseImage, UseBaseImage] = useState(DefaultImg);
const [DefaultImage, setDefaultImage] = useState("");
// function to upload image once it has been captured
setDefaultImage({
baseImage: DefaultImg
});
// function to capture base64 format of an image
function setGetBaseFile(files) {
// create a local readable base64 instance of an image
UseBaseImage({
baseImage: files.base64
});
let imageObj = {
imageName: "base-image-" + Date.now(),
imageData: files.base64.toString()
};
axios.post(`${API_URL}/image/uploadbase`, imageObj)
.then((data) => {
if (data.data.success) {
alert("Image has been successfully uploaded using base64 format");
UseBaseImage("base")
}
})
.catch((err) => {
alert("Error while uploading image using base64 format")
UseBaseImage("base")
});
}
return (
<div className="globalpost">
<Container className="mt-5 ml-auto mr-auto">
<h1 className="text-center">
Post to
<span className="text-success"> ShareVerse</span>
</h1>
<Form
className="shadow p-3 mb-5 bg-white rounded"
action="/search"
method="post"
encType="multipart/form-data"
>
<Form.Group controlId="formBasicVerse">
<Form.Label><h5>Upload Image</h5></Form.Label>
<FileBase type="file"
multiple={false}
onDone={setGetBaseFile}
/>
<Card.Img src={baseImage} alt="upload-image"></Card.Img>
</Form.Group>
</Form>
</Container>
</div>
);
};
export default GlobalPost;
Below is my mongoose data.schema
const mongoose = require("mongoose");
//create schema
const ImagesSchema = new mongoose.Schema({
name: {
type: String,
default: "none",
required: true
},
imageData: {
// type: mongoose.Types.ObjectId,
type: String,
required: true,
},
});
module.exports = mongoose.model("gallery", ImagesSchema);
Below is my app.js
// file includes all app level config, middleware, and supporting libraries
const express = require("express"); //import express
const app = express(); //initalise app with express
const cors = require("cors");
const bodyParser = require("body-parser");
const logger = require("morgan");
const routes = require("./routes/DataRoutes");
const ImageRouter = require('./routes/image');
app.use(bodyParser.json());
//body-parser handles HTTP POST requests.
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
//enable Cross-Origin Resource Sharing.
app.use(cors());
app.use(express.json());
// get log details of our app if needed
app.use(logger("dev"));
//middleware to import routes
app.use('/image', ImageRouter);
app.use("/", routes);
app.use('/search', express.static('search'));
module.exports = app;

Could not send file object from reactjs to nodejs

I am new to both nodejs and react.
I am working on sending a selected file from react(front end) to the node (back end) where I can upload the file and convert the file into json object. but when I try to access the selected file from request.body, it says the selectedFile is undefined.
Node code:
const express = require("express");
const bodyParser = require("body-parser");
const excelToJson = require("convert-excel-to-json");
const upload = require("express-fileupload");
const cors = require("cors");
const app = express();
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(upload());
let corsOptions = {
origin: "*",
optionsSuccessStatus: 200
};
app.use(cors(corsOptions));
app.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", "*");
res.header(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept"
);
next();
});
app.post("/upload", function(request, response, next) {
if (request.body.selectedFile) {
let file = request.body.selectedFile;
let dest = __dirname + "/uploads/sample.xlsx";
file.mv(dest, function(err) {
if (err) {
response.send("File not found");
} else {
const result = excelToJson({
sourceFile: "sample.xlsx"
});
response.json(result);
}
});
} else {
response.send("File not Found");
}
});
app.listen(4001, function() {
console.log("App is listening at port 4001");
});
React code:
import React from "react";
import axios from "axios";
import logo from "./logo.svg";
import "./App.css";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedFile: ""
};
this.handleFileUpload = this.handleFileUpload.bind(this);
this.handleUpload = this.handleUpload.bind(this);
}
handleFileUpload = function(event) {
this.setState({
selectedFile: event.target.files[0]
});
console.log(this.state.selectedFile);
};
handleUpload = function(event) {
event.preventDefault();
console.log(this.state.selectedFile);
let data = {
selectedFile: this.state.selectedFile
};
axios
.post("http://localhost:4001/upload", data)
.then(res => console.log(res))
.catch(err => console.log(err));
};
render() {
return (
<div>
<input
type="file"
name="fileSelected"
id="fileSelected"
onChange={this.handleFileUpload}
/>
<button type="submit" onClick={this.handleUpload}>
upload
</button>
</div>
);
}
}
export default App;
You can't send a file to JSON dialect API. But you can base64 encode the file, send it to the server and decode there. This isn't the best way, because it will increase file size while transferring to the backend, and you will spend additional resources to encode/decode it. As another option, you can use FormData to send the file to the server. For this option you need to have multipart/form-data parser in the backend, I'll suggest you using busboy

Axios to Node (Express) GET Request

So using axios, I am attempting to make a request to my express server, here is my Axios request:
/* #flow */
import type {
Dispatch,
GetState,
ThunkAction,
Reducer,
} from '../../types';
export const USERS_INVALID = 'USERS_INVALID';
export const USERS_REQUESTING = 'USERS_REQUESTING';
export const USERS_FAILURE = 'USERS_FAILURE';
export const USERS_SUCCESS = 'USERS_SUCCESS';
export const API_URL = '/api/articleList';
// Export this for unit testing more easily
export const fetchUsers = (axios: any, URL: string = API_URL): ThunkAction =>
(dispatch: Dispatch) => {
dispatch({ type: USERS_REQUESTING });
return axios.get(URL)
.then((res) => {
dispatch({ type: USERS_SUCCESS, data: res.data });
})
.catch((err) => {
dispatch({ type: USERS_FAILURE, err });
});
};
// Preventing dobule fetching data
/* istanbul ignore next */
const shouldFetchUsers = (state: Reducer): boolean => {
// In development, we will allow action dispatching
// or your reducer hot reloading won't updated on the view
if (__DEV__) return true;
const userListFetch = state.userListFetch;
if (userListFetch.readyStatus === USERS_SUCCESS) return false; // Preventing double fetching data
return true;
};
/* istanbul ignore next */
export const fetchUsersIfNeeded = (): ThunkAction =>
(dispatch: Dispatch, getState: GetState, axios: any) => {
/* istanbul ignore next */
if (shouldFetchUsers(getState())) {
/* istanbul ignore next */
return dispatch(fetchUsers(axios));
}
/* istanbul ignore next */
return null;
};
and here is my code on the express server:
//GET ARTICLES
app.get('/api/articleList', (req, res) => {
console.log('hello');
});
It does not log "hello" and it displays no errors, I just think I'm missing something for the Axios to reach my server...
I had something similar working on another app but cannot seem to implement it here, I've searched online and cannot find a solution, any help or advice is appreciated - thank you in advance!
NOTE: the Axios request works fine when the "API_URL" variable is
set to a myjson url link, so I know my action works fine, I just feel
like I'm missing something for it to reach my server
EDIT: Please see my entire server.js:
/* #flow */
import path from 'path';
import morgan from 'morgan';
import express from 'express';
import compression from 'compression';
import helmet from 'helmet';
import hpp from 'hpp';
import favicon from 'serve-favicon';
import React from 'react';
import { renderToString, renderToStaticMarkup } from 'react-dom/server';
import { StaticRouter } from 'react-router-dom';
import { matchRoutes } from 'react-router-config';
import { Provider } from 'react-redux';
import chalk from 'chalk';
import createHistory from 'history/createMemoryHistory';
import configureStore from './redux/store';
import Html from './utils/Html';
import App from './containers/App';
import routes from './routes';
import { port, host } from './config';
const app = express();
// Using helmet to secure Express with various HTTP headers
app.use(helmet());
// Prevent HTTP parameter pollution.
app.use(hpp());
// Compress all requests
app.use(compression());
// Use morgan for http request debug (only show error)
app.use(morgan('dev', { skip: (req, res) => res.statusCode < 400 }));
app.use(favicon(path.join(process.cwd(), './build/public/favicon.ico')));
app.use(express.static(path.join(process.cwd(), './build/public')));
// Run express as webpack dev server
if (__DEV__) {
const webpack = require('webpack');
const webpackConfig = require('../tools/webpack/webpack.client.babel');
const compiler = webpack(webpackConfig);
app.use(require('webpack-dev-middleware')(compiler, {
publicPath: webpackConfig.output.publicPath,
hot: true,
noInfo: true,
stats: 'minimal',
}));
app.use(require('webpack-hot-middleware')(compiler));
}
// Register server-side rendering middleware
app.get('*', (req, res) => {
if (__DEV__) webpackIsomorphicTools.refresh();
const history = createHistory();
const store = configureStore(history);
const renderHtml = (store, htmlContent) => { // eslint-disable-line no-shadow
const html = renderToStaticMarkup(<Html store={store} htmlContent={htmlContent} />);
return `<!doctype html>${html}`;
};
// If __DISABLE_SSR__ = true, disable server side rendering
if (__DISABLE_SSR__) {
res.send(renderHtml(store));
return;
}
// Load data on server-side
const loadBranchData = () => {
const branch = matchRoutes(routes, req.url);
const promises = branch.map(({ route, match }) => {
// Dispatch the action(s) through the loadData method of "./routes.js"
if (route.loadData) return route.loadData(store.dispatch, match.params);
return Promise.resolve(null);
});
return Promise.all(promises);
};
// Send response after all the action(s) are dispathed
loadBranchData()
.then(() => {
// Setup React-Router server-side rendering
const routerContext = {};
const htmlContent = renderToString(
<Provider store={store}>
<StaticRouter location={req.url} context={routerContext}>
<App />
</StaticRouter>
</Provider>,
);
// Check if the render result contains a redirect, if so we need to set
// the specific status and redirect header and end the response
if (routerContext.url) {
res.status(301).setHeader('Location', routerContext.url);
res.end();
return;
}
// Checking is page is 404
const status = routerContext.status === '404' ? 404 : 200;
// Pass the route and initial state into html template
res.status(status).send(renderHtml(store, htmlContent));
})
.catch((err) => {
res.status(404).send('Not Found :(');
console.error(`==> 😭 Rendering routes error: ${err}`);
});
});
//----------------------------------------------------
//GET ARTICLES
app.get('/api/articleList', (req, res) => {
console.log('yoyoyo');
var indexLimit = parseInt(req.query.indexLimit, 10);
var articleId = req.query.articleId
var articles = [];
db.collection('articles')
.find()
.sort("dateAdded", -1)
.limit(indexLimit)
.toArray()
.then(result => {
articles = articles.concat(result);
}).then(() => {
res.send(articles);
}).catch(e => {
console.error(e);
});
});
//------------------------------------
//connect to mongo db
var db
const MongoClient = require('mongodb').MongoClient
MongoClient.connect('mongodb://##CHANGED###:test#ds123930.mlab.com:###/###', (err, database) => {
if (err) return console.log(err);
db = database
console.log('db connected');
})
if (port) {
app.listen(port, host, (err) => {
if (err) console.error(`==> 😭 OMG!!! ${err}`);
console.info(chalk.green(`==> 🌎 Listening at http://${host}:${port}`));
// Open Chrome
require('../tools/openBrowser').default(port);
});
} else {
console.error(chalk.red('==> 😭 OMG!!! No PORT environment variable has been specified'));
}
You will need to move your /api routes above:
app.get('*', (req, res) => {
...
}
Your call to /api/articleList is hitting that catch all route handler of '*' and responding to the request with the rendered page. When communicating with your api for data, you don't want a page render, you want the response from the api :)
Middleware is executed in order of appearance from top to bottom.

Categories