Replace HTML form action with fetch api to webserver - javascript

I managed to store files on my pc using the HTML form action attribute and then handling this request on my Express webserver.
When I now try to replace this with an eventlistener on the submit button of the form instead of using the action attribute to send the post request I can not get it to work.
I get a error message 400 bad request.
Fetch
let form = document.querySelector('#uploadForm')
let inpFile = document.querySelector('#inpFile')
form.addEventListener('submit', async event => {
event.preventDefault()
const formData = new FormData()
formData.append('inpFile', inpFile.files[0])
fetch('http://myip/upload', {
method: 'POST',
headers: {
'Content-Type' : 'multipart/form-data'
},
body: formData
}).catch(console.error)
})
HTML
<form ref='uploadForm'
id='uploadForm'
method='post'
encType="multipart/form-data">
<input type="file" name="sampleFile" id="inpFile" />
<input type='submit' value='Submit' />
</form>
Express Server
const express = require('express')
const app = express();
const path = require('path')
const things = require('./routes/things')
const fileUpload = require('express-fileupload')
app.post('/upload', (req, res) => {
let sampleFile = req.files.sampleFile
sampleFile.mv(__dirname + '\\files\\' + sampleFile.name, (err) => {
if (err)
return res.status(500).send(err)
res.send('File uploaded!')
})
})

According to your html and fetch code your express code should looks like this:
const express = require('express')
const app = express();
const path = require('path')
const things = require('./routes/things')
const fileUpload = require('express-fileupload')
app.use('/upload', fileUpload({
createParentPath: true
}));
app.post('/upload', (req, res) => {
const { inpFile } = req.files;
inpFile.mv(path.join(__dirname, 'files', inpFile.name))
.then(() => res.send('File uploaded!'))
.catch(err => res.status(500).send(err));
})
You need to bind middleware to the application:
app.use('/upload', fileUpload({
createParentPath: true
}));
And your file object should be in req.files.inpFile.
Also you need to remove headers from your fetch request.

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);
}
});

Unexpected end of form error when using Multer

I'm trying to upload an image (jpg/jpeg/png) from the browser to NodeJS. I have read through several tutorials and many posts on forums but very few seem to have this specific issue.
I've made sure to match the name provided to multer (upload.single('upload')) with the formData key (formData.append('upload', selectedFile, selectedFile.name))
I tried using headers originally, but later read that I should exclude them.
I tried submitting through a <form action="/upload" method="post" enctype="multipart/form-data"> but still got the same error.
I have found this similar question with only one answer which isn't clear
Multer gives unexpetcted end of form error and this question Unexpected end of form at Multipart._final which has no answers.
Every other question seems to be about an 'Unexpected field' or 'Unexpected end of multipart data' error which - judging from the solutions - is irrelevant here.
Below is my code...
Browser:
<body>
<input type="file" id="file_uploader" name="upload" />
<button onclick="uploadImage()" class="btn-default">SUBMIT</button>
<!-- OTHER STUFF -->
</body>
<script>
let selectedFile;
let uploadData = new FormData();
const fileInput = document.getElementById('file_uploader');
fileInput.onchange = () => {
selectedFile = fileInput.files[0];
uploadData.append('upload', selectedFile, selectedFile.name);
}
function uploadImage(){
fetch('/upload', {
method: 'POST',
body: uploadData
})
.then((response) => {
console.log(response);
})
.catch((error) => {
console.error('Error: ', error);
});
}
</script>
NodeJS
let express = require('express');
const multer = require('multer');
//multer options
const upload = multer({
dest: './upload/',
limits: {
fileSize: 1000000,
}
})
const app = express();
app.post('/upload', upload.single('upload'), (req, res) => {
res.send();
}, (error, req, res, next) => {
console.log(error.message);
})
exports.app = functions.https.onRequest(app);
...And here is the error log, if it helps:
Error: Unexpected end of form
> at Multipart._final (C:\Users\p\Downloads\MyInvestmentHub\functions\node_modules\busboy\lib\types\multipart.js:588:17)
> at callFinal (node:internal/streams/writable:694:27)
> at prefinish (node:internal/streams/writable:723:7)
> at finishMaybe (node:internal/streams/writable:733:5)
> at Multipart.Writable.end (node:internal/streams/writable:631:5)
> at onend (node:internal/streams/readable:693:10)
> at processTicksAndRejections (node:internal/process/task_queues:78:11)
I haven't posted many questions as of yet, so I apologise if I'm missing something or the format is off. Let me know and I will make appropriate edits.
Thanks.
I also got the exact same error.
Before using multer I had installed express-fileupload. When I unistalled it using the command npm uninstall express-fileupload I could get rid of the error.
And if it is the same case with you don't forget to delete the commands you already added for express-fileupload module. (like requiring fileupload)
Hi there I ran into the same issue for me was the lack of a bodyParser middleware that allows our requests files to parsed into Buffers.
I was able to resolve the problem like so in express:
var bodyParser = require('body-parser')
bodyParser.json([options])
I had this problem using multer with next js api. What worked for me is, I exported an a config that sets bodyParser to false like so;
export const config = {
api: {
bodyParser: false
}
}
In my case, the cause was other middleware. Check for other middleware running before multer. For me, the issue was express-openapi-validator middleware. Once I removed that middleware, it worked as expected.
Using body-parser package worked for me:
const bodyParser = require('body-parser')
// ...
app.use(bodyParser()) // support encoded bodies
My upload single file route:
const multer = require('multer')
const express = require('express')
const router = express()
const path = require('path') // node built-in path package
// needs "app.use(bodyParser())" middleware to work
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, process.cwd() + '/public/')
},
filename: function (req, file, cb) {
// generate the public name, removing problematic characters
const originalName = encodeURIComponent(path.parse(file.originalname).name).replace(/[^a-zA-Z0-9]/g, '')
const timestamp = Date.now()
const extension = path.extname(file.originalname).toLowerCase()
cb(null, originalName + '_' + timestamp + extension)
}
})
const upload = multer({
storage: storage,
limits: { fileSize: 1 * 1024 * 1024 }, // 1 Mb
fileFilter: (req, file, callback) => {
const acceptableExtensions = ['png', 'jpg', 'jpeg', 'jpg']
if (!(acceptableExtensions.some(extension =>
path.extname(file.originalname).toLowerCase() === `.${extension}`)
)) {
return callback(new Error(`Extension not allowed, accepted extensions are ${acceptableExtensions.join(',')}`))
}
callback(null, true)
}
})
router.post('/api/upload/single', upload.single('file'), (req, res) => {
res.status(200).json(req.file)
})
module.exports = {
uploadRouter: router
}
I think this is may causes by the responsed end,so in your continuous Middleware,you can do upload file at last.
i do this resolve problems.
const upload = multer({
dest: "./uploads",
});
app.use(upload.any());
app.post(
"/upload",
(req, res, next) => {
res.end("文件上传成功");
},
upload.single("fileKey")
);
try using these it work
const express = require('express')
const app = express()
const path = require('path')
const multer = require('multer')
var filestorageEngine = multer.diskStorage({
destination: (req, file, cb) => {
cb(null,'./uploads')
},
filename:(req,file, cb) => {
cb(null,"[maues]-" + file.originalname)
}
})
var upload = multer({
storage:filestorageEngine
})
app.post('/file', upload.array('file', 3),(req, res) => {
console.log(req.file)
res.send("file uploaded successfully")
})
app.listen(5000, ()=> {
console.log("server running")
})
in my frontend or client-side removing the headers in my request. And make sure your inputs are as a formData.
For example:
let formData = new FormData();
formData.append("fileName", file);
const res = await fetch("/api/create-card", {
method: "POST",
body: formData,
})
This worked for me.
I think, the problem is in the express and body-parser module, I just eliminated it
app.use(bodyParser.text({ type: '/' }));
and it works!
Try downgrading Multer to 1.4.3. It worked for me.
See https://github.com/expressjs/multer/issues/1144

POST data passed from frontend JS to Nodejs/Expressjs is always undefined

I have a frontend JS script that takes text input from an HTML text box and sends it to an expressjs server. The body of the POST request, though, is always undefined, or depending on how I tweak things, returning as "{ }" if I view it via console.log( ). As I'm new to this, I can't seem to see what's going wrong.
Front end js:
async function submitCity(){
let x = document.getElementById("wg_input").value;
console.log("Successfully captured city name:", x);
let toWeather = JSON.stringify(x);
console.log("Input data successfully converted to JSON string:", toWeather);
const options = {
method: 'POST',
mode: 'cors',
headers: {'Content-Type': 'text/plain'},
body: toWeather
}
fetch('http://localhost:3000', options)
.then(res => console.log(res))
.catch(error => console.log(error))
}
Backend:
// Dependencies
const express = require('express');
const bp = require("body-parser");
const request = require("request");
const jimp = require('jimp');
const cors = require('cors');
const wgServer = express();
const port = 3000;
// Dotenv package
require("dotenv").config();
// OpenWeatherMap API_KEY
const apiKey = `${process.env.API_KEY}`;
// Basic server initialization
wgServer.use(cors())
wgServer.use(bp.json())
wgServer.use(bp.urlencoded({ extended: true }))
wgServer.listen(port, function() {
console.log(`Example app listening on port ${port}!`)
});
wgServer.post('/', async function (req, res) {
res.set('Content-Type', 'text/plain');
console.log(req.body)
res.send('Hello World');
//const data = await req.body;
// let jsonData = JSON.stringify(req.body);
// res.status(201);
//res.json();
});
The returned data is supposed to be a string of about 15 characters, give or take a few (a city and state). I thank you in advance.

node express return request body empty by using API fetch POST

Learning nodejs and trying to post a form from HTML (with image upload) to nodejs (express) but the request.body returning empty object.
Tried few solutions on this site but no one is working.
Here is my code for creating a dynamic form. (HTML)
function show(data) {
const d = data.temp_form;
let content = ''
// console.log(d)
d.forEach((item) => {
if (item === 'image_backup' || item === 'image_banner') {
content += `<label for='${item}'>${item}</label><input name='${item}' type='file' id='${item}' value=''><br/>`
}else{
content += `<label for='${item}'>${item}</label><input name='${item}' type='text' id='${item}' value=''><br/>`
}
})
content += ` <input type="submit" id="handle_submit">`
getFormContianer.innerHTML = content
}
Code handling form submit
async function handleForm(e) {
e.preventDefault();
let dataForm = new FormData(e.target)
let obj = {}
dataForm.forEach((value, key) => {
obj[key] = value
if( typeof value === 'object'){
console.log(value.name)
obj[key] = value.name
}
});
let data = JSON.stringify(obj);
await fetch(file_api, {
method: 'POST',
body: data
}).then((res)=>{
return res.json();
}).then((data)=>{
console.log('api err: '+data);
}).catch((err) =>{
console.log('api err: '+ err)
})
}
Then, in my nodejs
const express = require('express');
const cors = require('cors');
const config = require('./config')
var bodyParser = require('body-parser');
var multer = require('multer');
var upload = multer();
const app = express()
const templates = require('./routes/templates-routes');
const files = require('./routes/files-routes');
app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(upload.array());
app.use(express.static('public'))
app.use('/api', templates.routes);
app.use('/create', files.routes);
app.listen(config.port, () => {
console.log(`Example app listening at http://localhost:${config.port}`)
})
and in the route.js
const express = require('express');
const router = express.Router();
const {replaceValue } = require('../controllers/filesController');
router.post('/file', replaceValue);
module.exports={
routes: router
}
for the fileController.js
const replaceValue = (request, response) =>{
console.log(request.body)
response.send(request.body)}
Hope that can get some comment for you, thank you so much!
let data = JSON.stringify(obj);
await fetch(file_api, {
method: 'POST',
body: data
You are passing a string to body and haven't specified a Content-Type header so fetch will generate a Content-Type: text/plain header.
Since plain text isn't JSON, the JSON parsing middleware you have set up in Express won't process it.
await fetch(file_api, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: data
Note that this will make it a preflighted request, so make sure you follow the instructions for the CORS module to support that.

Trigger a download in Koa request handler

I'm trying to trigger a download from a POST request handler in Koa with koa-router. Essentially, I'm trying to do something like this:
app.js
const Koa = require('koa')
const router = require('./router')
const app = new Koa()
app.use(router.routes())
app.use(router.allowedMethods())
app.listen(3000)
router.js
const fs = require('fs')
const Router = require('koa-router')
const router = new Router()
router.post('/generate', function * () {
const path = `${__dirname}/test.txt`
this.body = fs.createReadStream(path)
this.set('Content-disposition', 'attachment; filename= test.txt')
})
module.exports = router
client.js
const { fetch } = window;
const request = {
method: 'POST',
body: JSON.stringify({ fake: 'data' })
}
// Make the POST request
fetch('/generate', request)
However, when the POST request is sent, nothing happens. I don't get any error in the server console or the browser console either. Any help would be appreciated!
You should set file stream in the body and send Content-disposition to attachment with that file name. Use below code
const Router = require('koa-router');
const router = new Router();
router.post('/generate', function * () {
const path = `${__dirname}/file.txt`;
this.body = fs.createReadStream(path);
this.set('Content-disposition', 'attachment; filename= file.txt');
});
module.exports = router;
UPDATE: Complete working code:
var app = require('koa')();
var router = require('koa-router')();
const fs = require('fs');
router.post('/generate', function () {
const path = `${__dirname}/file.txt`;
this.body = fs.createReadStream(path);
this.set('Content-disposition', 'attachment; filename= file.txt');
});
app
.use(router.routes())
.use(router.allowedMethods());
app.listen(3000);
Client:
<button id="btnDownload">Download</button>
<script type="text/javascript">
const request = {
method: 'POST',
body: JSON.stringify({
fake: 'data'
})
}
document.getElementById('download').onclick = () => {
fetch('/generate', request)
.then(res => {
return res.text()
})
.then(content => {});
}
</script>
You could try using https://github.com/koajs/send
router.post('/generate', function * (next) {
yield send(this, 'file.txt');
});
And in client side, you'll need to create and trigger download upon receiving file content via post request. Put this code in request callback
fetch('/generate', request)
.then(res => { return res.text() })
.then(content => {
uriContent = "data:application/octet-stream," + encodeURIComponent(content);
newWindow = window.open(uriContent, 'somefile');
});
same functionality can be achieved using a tag download.I prefer this.It works without JS but not in safari.
<!DOCTYPE html>
<html>
<body>
<p>Click on the w3schools logo to download the image:<p>
<a href="http://www.w3schools.com/images/myw3schoolsimage.jpg" download>
<img border="0" src="http://www.w3schools.com/images/myw3schoolsimage.jpg" alt="W3Schools" width="104" height="142">
</a>
<p><b>Note:</b> The download attribute is not supported in Edge version 12, IE, Safari or Opera version 12 (and earlier).</p>
</body>
</html>
refernce:
http://www.w3schools.com/tags/att_a_download.asp

Categories