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
Related
//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
i find evrywhere solution white :
app.use(bodyParser.urlencoded({extended: true}));
i can use
JSON.stringify(req.files)
but im sur having a way to fix my problem
my mocha test :
it('a file', async function () {
const body = { pseudo: 'user', password: 'test#123', mail: 'supermail' };
const response = await fetch(hostName + '/authentication/register', {
method: 'post',
body: JSON.stringify(body),
headers: { 'Content-Type': 'application/json' }
})
const usr = await response.json();
request.post('/usrAction1/doc')
.field('token', usr.token)
.attach('file', 'test/test.pdf')
.end(function (err, res) {
if (err) {
console.log(err)
}
console.log(res.status) // 'success' status
});
});
and my rout handler :
router.post('/doc', async (req, res) => {
console.log('req.files');
console.log(req.files)
})
also my server.js:
import express from 'express'
import authentication from './src/login.js'
import './global/host.js'
import bodyParser from 'body-parser'
import cors from "cors"
import verifyToken from './middleware/auth.js'
import { userAction1, userAction2 } from './src/userAction.js'
import verifyLevel from './middleware/level.js'
import fileUpload from 'express-fileupload';
export default function myApp() {
const whitelist = [/http:\/\/localhost:*/, /http:\/\/127.0.0.1:*/]
const corsConfig = { origin: whitelist }
const app = express();
const port = hostPort;
//json encoded
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json())
app.use(cors(corsConfig))
// enable files upload
app.use(fileUpload({
createParentPath: true
}));
app.use('/usrAction1', userAction1())
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`);
});
return app;
}
myApp();
but don't to work for me .
i also test white external client server who juste runing a form and send it to my tested adress and
do the same [Object null prototype]
thank u for evry litel help
i waiting of some help i using this magique code i found somwhere on stackoverflow:
req.files && Object.keys(req.files)?.map((obj, idx) => { console.log(req.files['file'].data) })
if somone have a better idea i waiting .
thank to all
How would I go about rendering a component after an axios POST request? I want to load a new component after the success response has been received from Stripe. I am trying to update the state of my component by adding a setState after receiving the response and load a simple div if the state has any values. The issue I am having is that component is not re-rendering when I use the setState.
Below is how I have a stripe component setup and the express server:
import StripeCheckout from 'react-stripe-checkout';
import axios from 'axios';
import './stripe-button.styles.scss';
import { createStructuredSelector } from 'reselect';
import { selectCurrentUser } from '../../redux/user/user.selectors';
import { setCurrentUser } from '../../redux/user/user.actions';
class StripeCheckoutButton extends React.Component {
constructor(props) {
super(props);
this.state = {
cardListBacklog: []
};
}
onToken = token => {
console.log(token);
const { cartItems, price } = this.props;
const priceForStripe = price * 100;
const orderSummary = cartItems.reduce(
(cartItemAll, cartItem) =>
(cartItemAll += cartItem.name + cartItem.quantity),
''
);
axios({
url: 'payment',
method: 'post',
data: {
amount: priceForStripe,
order: orderSummary,
token
}
})
.then(response => {
alert(
`Payment successful, ${response.data.success.billing_details.name}; please check your email for your receipt.`
);
this.setState({cardListBacklog: response.data});
})
.catch(error => {
console.log('Payment error: ', JSON.parse(error));
alert('There was an issue with your payment. Please try again!');
});
};
render() {
const publishableKey = 'pk_test_gxxxxxxxxxxxxxxxxxxxxxxxxxxx';
const { price } = this.props;
const priceForStripe = price * 100;
return (
this.state.cardListBacklog.length
?
<div>Payment Successful</div>
:
<StripeCheckout
label="Pay Now"
name="Ltd."
billingAddress
shippingAddress
image="https://i.imgur.com/vWgUzv.png"
description={`Your total is $${price} USD`}
amount={priceForStripe}
panelLabel="Pay Now"
token={this.onToken}
stripeKey={publishableKey}
label="Pay with 💳"
/>
);
}
}
export default StripeCheckoutButton;
Here is my Server.js:
const express = require('express');
const cors = require('cors');
const bodyParser = require('body-parser');
const path = require('path');
if (process.env.NODE_ENV !== 'production') require('dotenv').config();
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const app = express();
const port = process.env.PORT || 5000;
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cors());
if (process.env.NODE_ENV === 'production') {
app.use(express.static(path.join(__dirname, 'client/build')));
app.get('*', function(req, res) {
res.sendFile(path.join(__dirname, 'client/build', 'index.html'));
});
}
app.listen(port, error => {
if (error) throw error;
console.log('Server running on port: ' + port);
});
app.post('/payment', (req, res) => {
const body = {
source: req.body.token.id,
amount: req.body.amount,
receipt_email: req.body.token.email,
description: req.body.order,
currency: 'usd'
};
stripe.charges.create(body, (stripeErr, stripeRes) => {
if (stripeErr) {
res.status(500).send({ error: stripeErr });
} else {
res.status(200).send({ success: stripeRes });
}
});
});
this.state.cardListBacklog.length
This is the issue. Its giving 0 before update, and undefined after its updated. Console log and check if its true.
this.state = {
cardListBacklog: false
};
and
this.setState({cardListBacklog: true});
should do the trick.
I guess, you expects stripeRes in the state, but you are receiving an object
{success: stripeRes} instead.
You response with an object from the server here
res.status(200).send({ success: stripeRes });
But on the client side in the state you expect the array, not an object.
this.state.cardListBacklog.length
Object doesn't have length property by default.
You should check something else on the client. Maybe you should update state
on success response like
this.setState({cardListBacklog: response.data.success });
This is not super cool, but should give you an idea that client side and server side expect different things.
You should rethink your API.
The idea to use flag of successful response here (https://stackoverflow.com/a/59011695/10559239) makes sense to you, if you doesn't want to use response data in near future. Good as a first step.
But the main problem, as I can see is inconsistency between server and client.
I totally new to ReactJS.
And I follow this tutorial in YouTube and I follow each steps.
Until I found my code got an error like this
Since I new to programming ReactJS, I still dont understand what to do, and how can I fix this
The tutorial shows how to build a simple CRUD app built in ReactJS and PostgreSQL
Here I provide my App.js code
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
class App extends Component {
constructor(){
super();
this.state={
title: 'Simple Guestbook',
guestbooks: []
}
}
componentDidMount(){
console.log('Component has mounted')
}
addGuest(event){
event.preventDefault();
let data = {
guestname: this.refs.guestname.value,
guestaddress: this.refs.guestaddress.value
};
var request = new Request("http://localhost:3000/api/new-guest", {
method: 'POST',
headers: new Headers({'Content-Type': 'application/json'}),
body: JSON.stringify(data)
});
//xmlhttprequests
fetch(request)
.then(function(response){
response.json()
.then(function(data){
console.log(data)
})
})
}
render() {
let title = this.state.title;
return (
<div className="App">
<h1>Assalaamu'alaykum</h1>
<h2>{title}</h2>
<form ref="guestbookForm">
<input type="text" ref="guestname" placeholder="guest name"/>
<input type="text" ref="guestaddress" placeholder="guest address"/>
<button onClick={this.addGuest.bind(this)}>Add Guest</button>
</form>
</div>
);
}
}
export default App;
And here is my server.js code:
let express = require('express');
let bodyParser = require('body-parser');
let morgan = require('morgan');
let pg = require('pg');
const PORT = 3000;
let pool = new pg.Pool({
port:5432,
password: 'qwerty',
database: 'guestbook',
max: 10,
host: 'localhost',
user: 'postgres'
});
let app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.use(morgan('dev'));
app.use(function(request, response, next) {
response.header("Access-Control-Allow-Origin", "*");
response.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
app.post('/api/new-guest', function(request, response){
console.log(request.body);
});
app.listen(PORT, () => console.log('Listening to Port '+ PORT));
What should I do? Any suggest will help me to solve this
Thank you...
Use Arrow funtion,
fetch(request)
.then((response) => {
return response.json()
}).then((data) => {
console.log(data)
})
I am working in a reactjs application where i have to upload user image. I am getting file on onChange event of file input and passing it parent component and parent component will make a post request using the data
Server side I am using express and multer for file upload and client side using fetch api to upload the image.
Thanks in advance :)
I figure it out
To upload an file/image to multer we need a form enctype="multipart/form-data" without that it wont work with multer
I am getting file from a child component then
1) i have created a empty form with the encType="mutipart/form-data"
2) when i received the file i create a new FormData(with ref to myform)
3) then append key and value in the formData
4) create fetch.post() and it works :)
for ref submitting the code
React Parent component Upload.js
import React, { Component } from 'react'
import { ImageWithoutForm } from "../app/components/ImageUpload";
export default class UploadFile extends Component {
onImageLoad(e){
console.log('onImageLoad', e.target.files[0]);
this.uploadForm(e.target.files[0]);
}
uploadForm(file){
let form = new FormData(this.refs.myForm);
form.append('myImage', file);
fetch('/upload-image', {
method: 'POST',
body: form
}).then(res => console.log('res of fetch', res));
}
render() {
return (
<div>
<h4>Upload Image</h4>
<ImageWithoutForm onImageLoad={(e)=>this.onImageLoad(e)} />
<form id="upload_form" ref="myForm" encType="multipart/form-data">
</form>
</div>
)
}
}
React Child Component with input to load the file ImageWithoutForm.js
import React, { Component } from 'react'
export class ImageWithoutForm extends Component {
handleSubmit(e){
this.props.onImageLoad(e);
}
render() {
return (
<div>
<input type="file" onChange={(e)=>this.handleSubmit(e)}/>
</div>
)
}
}
Express Route file taken from someone github repo and customized UploadImage.js
const express = require('express');
const multer = require('multer');
const path = require('path');
// Set Storage Engine
const storage = multer.diskStorage({
destination: './public/uploads/',
filename: function(req, file, cb){
cb(null,file.fieldname + '-' + Date.now() + path.extname(file.originalname));
}
});
// Init Upload
const upload = multer({
storage: storage,
limits:{fileSize: 1000000},
fileFilter: function(req, file, cb){
checkFileType(file, cb);
}
}).single('myImage');
// Check File Type
function checkFileType(file, cb){
// Allowed ext
const filetypes = /jpeg|jpg|png|gif/;
// Check ext
const extname = filetypes.test(path.extname(file.originalname).toLowerCase());
// Check mime
const mimetype = filetypes.test(file.mimetype);
if(mimetype && extname){
return cb(null,true);
} else {
cb('Error: Images Only!');
}
}
// Init app
const app = express.Router();
// Public Folder
app.use(express.static('./public'));
app.post('/', (req, res) => {
console.log('handling upload image');
upload(req, res, (err) => {
if(err){
console.log('first err', err);
res.send({
msg: err
});
} else {
if(req.file == undefined){
console.log('Error: No File Selected!')
res.send({
msg: 'Error: No File Selected!'
});
} else {
console.log('File Uploaded!')
res.send({
msg: 'File Uploaded!',
file: `uploads/${req.file.filename}`
});
}
}
});
});
module.exports = app;
and in my express app.js just require the route file ImageUpload.js
and map to the route like this
var uploadImage = require('./routes/UploadImage');
server.use('/upload-image', uploadImage);