facing some issue uploading file and data with multer in express server - node.js

I have 2 problems. I am trying to upload an excel file to the server when I upload an excel file I got the file on my console log but the file is not saving into the folder which I declared on my storage variable.
// Middleware
app.use(express.json())
app.use(cors());
app.use(fileupload());
app.use(express.static("files"));
app.use(bodyParser.json({ limit: "50mb" }));
app.use(bodyParser.urlencoded({ limit: "50mb", extended: true, parameterLimit: 50000 }));
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/');
},
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now() + path.extname(file.originalname));
}
});
var upload = multer({ storage: storage })
File upload API. The 2nd problem is I have sent some data and a file from frontend. I am getting the file only but on my req.body I got undefined. If I remove the upload.single('file') then I got the other data.
app.post("/upload-excel", upload.single('file'), async (req, res) => {
const vendor = req.body
const file = req.files.file;
const filename = file.name;
console.log(vendor);
})
Here is my frontend:
const ProductImport = function () {
const [file, setFile] = useState<any>();
const [isLoading, setIsLoading] = useState<boolean>(false);
const [fileName, setFileName] = useState("");
const { userDetails } = UseAuth()
const saveFile = (e) => {
setFile(e.target.files[0]);
setFileName(e.target.files[0].name);
};
const uploadFile = async (e: any) => {
e.preventDefault()
const formData = new FormData();
formData.append("file", file);
formData.append("fileName", fileName);
formData.append("vendor", userDetails.role);
formData.append("store", userDetails.store);
formData.append("publisher", userDetails.email);
fetch('http://localhost:5000/upload-excel', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.insertedId) {
alert('excel Added')
}
})
.catch(error => {
console.error('Error:', error);
});
};
return (
<form onSubmit={uploadFile}>
<label htmlFor="formGroupExampleInput" className="form-label">Example label</label>
<input type="file" onChange={saveFile} />
<button type="submit">Upload</button>
</form>
)
}
backend folder structure

Related

How to upload a file using Multer in a specific directory defined by the frontend in vuejs

I want to upload a file using Multer in a specific directory in my NodeJs app defined by the frontend in vuejs.
It is my first application with multer
The backend code is :
...
const app = express();
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
const storage = multer.diskStorage({
destination: (req, file, cb) => {
//req.body.idUser is undefined here
let destinationPath = path.join("upload_file", req.body.idUser);
if (!fs.existsSync(destinationPath)) {
fs.mkdirSync(destinationPath);
}
cb(null, destinationPath);
},
filename: (req, file, cb) => {
let newFileName = Date.now() + path.extname(file.originalname);
cb(null, newFileName);
},
});
const upload = multer({ storage });
app.post(
"/file/add",
upload.fields([{ name: "newfile" }, { name: "idUser" }]),
(req, res) => {
res.json({
response: "file uploaded",
});
},
);
...
And the frontend code is :
...
async sendDocument(event){
const file = event.target.files[0]
const form = new FormData()
form.append("newfile", file, file.name)
form.append("idUser", this.getIdUser)
const opts =
{
method: "POST",
body: form,
}
const url = "http://localhost:3000/file/add";
try{
await fetch(url, opts)
.then(function (response) {
return response.json();
})
.then(function (res) {
console.log(res)
});
}catch(err){
console.log(err)
}
},
...
I tried to debug step by step with console.log to check why req.body.idUser is not defined in storage and I need it to complete the destinationPath
If I replace req.body.idUser by static value like "toto", all work fine
In front, this.getIdUser is working fine. And req.body.idUser is working in app.post
Thanks for your help
Try appending the ID to the form first:
const form = new FormData()
form.append("idUser", this.getIdUser)
form.append("newfile", file, file.name)

Undefined For image upload in React Typescript, Backend -Express Multer

I am able to upload image using Postman in DB and in the destination folder but when I am trying to upload it via frontend(React Typescript) it shows undefined for the image. Can anyone have a look at it and let me know what I am doing wrong. I am stuck at this for hours.
React Typescript Code
function ImageUpload() {
const [images, setImages] = useState<File>();
const handleInput = (event: ChangeEvent<HTMLInputElement>) => {
// setImages(event.target.files[0])
const fileList = event.target.files;
// approach 1
if (!fileList) return;
else {
console.log(fileList[0].type)
setImages(fileList[0]);
}
}
const handleSubmit = (event: FormEvent) => {
event.preventDefault()
console.log("Before axios")
console.log(images)
const config = {
headers: {
"content-Type": "multipart/form-data"
}
};
//approach 1
Axios.post("http://localhost:3003/imageInsert", images).then(() => {
alert("Image Inserted 1")
}).catch(error => {
console.log(error.response)
})
}
return (
<>
<div>
<form onSubmit={handleSubmit} encType="multipart/form-data">
<div>
<label>Image </label><br /><br />
<input type="file" name='image' accept='image/*' onChange={handleInput} />
</div>
<br />
<button type='submit'>Submit</button>
</form>
</div>
</>
)
}
export default ImageUpload
Express Code
const express = require('express')
const app = express()
const mysql = require('mysql2')
const bodyParser = require('body-parser')
const cors = require('cors')
const path = require('path')
const multer = require('multer')
const db = mysql.createPool({
host: 'localhost',
user: 'xyz',
password: 'xyz',
database: 'demo',
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0
})
app.use(cors())
app.use(express.json())
const fileStorageEngine = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "images");
},
filename: (req, file, cb) => {
console.log(file)
cb(null, Date.now() + "--" + file.originalname);
},
})
const upload = multer({ storage: fileStorageEngine })
app.post("/imageInsert", upload.single('image'), (req, res) => {
console.log(req.file) // shows undefined here
const filename = req.file.filename
const query = `insert into image (img) values ('${filename}')`;
db.query(query, (error, result) => {
res.send("Image Inserted in DB" + Date.now())
console.log("Image Inserted")
})
})
app.listen(3003, () => {
console.log("running on port 3003")
});

File uploads but the multer code is not executed | Node + multer + express + formdata

I am uploading a video file to a local folder using form, multer, express and nodejs.
The video file gets uploaded to the local folder - everytime I upload it through the form. However, the code inside upload executes only occasionally. Once in 5 times. i.e. console.log('33'); inside the app.post doesn't always get printed. However, console.log('1') (the one inside post and before upload) works everytime.
server.js code
var Express = require('express');
var multer = require('multer');
var mkdirp = require('mkdirp');
var app = Express();
var cors = require('cors');
app.use(cors());
var Storage = multer.diskStorage({
destination: function(req, file, cb) {
var dir = './client/public/video/';
mkdirp(dir, function(err) {
if(err) {
console.error(err);
}
cb(null, dir);
});
console.log("Upload: saved to " + dir + file.originalname);
},
filename: function(req, file, callback) {
callback(null, file.fieldname + "_" + Date.now() + "_" + file.originalname);
}
});
var upload = multer({
storage: Storage
}).single("file");
app.post("/api", function(req, res) {
console.log('1');
upload(req, res, function(err) {
console.log('33');
if (err) {
return res.end("Something went wrong!");
}
return res.status(200).end("File uploaded successfully!.");
});
});
var server = app.listen(9000, function () {
console.log('app listening at 9000');
});
app.js code
import React, { Component } from "react";
import axios from "axios";
class App extends Component {
state = {
file: null
};
handleOnChange = e => this.setState({ [e.target.name]: e.target.value });
handleOnUploadFile = e => this.setState({ file: e.target.files[0] });
handleOnSubmit = e => {
e.preventDefault();
const formData = new FormData();
formData.append("file", this.state.file);
axios
.post("http://localhost:9000/api", formData, {
headers: {
'accept': 'video/mp4',
'Accept-Language': `en-US,en;q=0.8`,
'Content-Type': `multipart/form-data; boundary=${formData._boundary}`,
}
})
.then(res => console.log(res.data))
.catch(err => console.error(err));
};
render() {
return (
<form>
<input type="file" encType="multipart/form-data"
name="file"
accept="video/mp4"
onChange={this.handleOnUploadFile}/>
<button type="submit" className="btn btn-danger" onClick={this.handleOnSubmit}>
Submit
</button>
</form>
);
}
}
export default App;
I am new to react/node js. Tried a lot of suggestions from other posts, but couldn't find the solution.
Try adding async and await.
server.js
var Express = require('express');
var multer = require('multer');
var mkdirp = require('mkdirp');
var app = Express();
var cors = require('cors');
app.use(cors());
var Storage = multer.diskStorage({
destination: function(req, file, cb) {
var dir = './client/public/video/';
mkdirp(dir, function(err) {
if(err) {
console.error(err);
}
cb(null, dir);
});
console.log("Upload: saved to " + dir + file.originalname);
},
filename: function(req, file, callback) {
callback(null, file.fieldname + "_" + Date.now() + "_" + file.originalname);
}
});
var upload = multer({
storage: Storage
}).single("file");
app.post("/api", function(req, res) {
console.log('1');
upload(req, res, function(err) {
console.log('33');
if (err) {
return res.end("Something went wrong!");
}
return res.status(200).end("File uploaded successfully!.");
});
});
app.get("/", function(req, res) {
console.log('1');
return res.status(200).json({});
});
var server = app.listen(9900, function () {
console.log('app listening at 9900');
});
App.js
import React, { Component } from "react";
import axios from "axios";
class App extends Component {
state = {
file: null
};
handleOnChange = e => this.setState({ [e.target.name]: e.target.value });
handleOnUploadFile = e => this.setState({ file: e.target.files[0] });
handleOnSubmit = async e => {
e.preventDefault();
const formData = new FormData();
formData.append("file", this.state.file);
await axios
.post("http://localhost:9900/api", formData, {
headers: {
'accept': 'video/mp4',
'Accept-Language': `en-US,en;q=0.8`,
'Content-Type': `multipart/form-data; boundary=${formData._boundary}`,
}
})
.then(res => console.log(res.data))
.catch(err => console.error(err));
};
render() {
return (
<>
<h1>{"<b>hello</b>".bold()}</h1>
<form>
<input type="file" encType="multipart/form-data"
name="file"
accept="video/mp4"
onChange={this.handleOnUploadFile}/>
<button type="submit" className="btn btn-danger" onClick={this.handleOnSubmit}>
Submit
</button>
</form>
</>
);
}
}
export default App;
Try using a promise for your upload function:
var Express = require('express');
var multer = require('multer');
var mkdirp = require('mkdirp');
var app = Express();
var cors = require('cors');
app.use(cors());
var Storage = multer.diskStorage({
destination: function(req, file, cb) {
var dir = './client/public/video/';
mkdirp(dir, function(err) {
if(err) {
console.error(err);
}
cb(null, dir);
});
console.log("Upload: saved to " + dir + file.originalname);
},
filename: function(req, file, callback) {
callback(null, file.fieldname + "_" + Date.now() + "_" + file.originalname);
}
});
var upload = multer({
storage: Storage
}).single("file");
app.post("/api", function(req, res) {
console.log('1');
upload(req, res).then((file, err) => {
console.log('33');
if (err) {
return res.end("Something went wrong!");
}
return res.status(200).end("File uploaded successfully!.");
})
});
var server = app.listen(9000, function () {
console.log('app listening at 9000');
});
Another idea would be to wrap the upload itself in a promise:
var Storage = multer.diskStorage({
return new Promise((resolve, reject) => {
destination: function(req, file, cb) {
var dir = './client/public/video/';
mkdirp(dir, function(err) {
if(err) {
reject(err)
}
cb(null, dir);
});
console.log("Upload: saved to " + dir + file.originalname);
},
filename: function(req, file, callback) {
resolve(callback(null, file.fieldname + "_" + Date.now() + "_" + file.originalname);)
}
})
});

How to Upload File from angular7 to Node.js server

My node.js code is working 100% by Postman,
but in angular when I send a FormData and nothing happened in server, no error but the file not uploaded.
how can i upload file by angular?
HTML Compnent:
<form>
<input type="file" (change)="OnFileSelected($event)" name='photo'>
<button type='submit' (click)="OnUpload()">Save Post</button>
</form>
Ts Compnent:
OnFileSelected(event){
this.SelectedFile = event.target.files[0] as File;
}
OnUpload(){
const form: FormData = new FormData();
form.append('photo', this.SelectedFile, this.SelectedFile.name);
//Convert to Json because 'Unexpected token - in JSON at position 0 at JSON.parse' error in server
let responseBody: {} = JSON.stringify(form);
this.http.post(URL, responseBody).subscribe(event => { console.log(event)}, err => {console.log(err)
});
}
Node.js Server:
const path = require('path');
const express = require('express');
const multer = require('multer');
const bodyParser = require('body-parser')
const router = express.Router();
const DIR = './uploads';
let storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, DIR);
},
filename: (req, file, cb) => {
cb(null, file.fieldname + '-' + Date.now() + path.extname(file.originalname));
}
});
let upload = multer({ storage: storage });
router.use(bodyParser.json());
router.use(bodyParser.urlencoded({ extended: true }));
router.use(function (req, res, next) {
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:4200');
res.setHeader('Access-Control-Allow-Methods', 'POST');
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
res.setHeader('Access-Control-Allow-Credentials', true);
next();
});
router.post('/upload-image', upload.single('photo'), async function (req, res) {
console.log(req.body);
if (!req.file) {
console.log("No file received");
return res.send({
success: false
});
} else {
console.log('file: ',req.file);
return res.send({
success: true
})
}
});
You need to specify that your Request is a form-data request:
// Create Form Data
const formData: FormData = new FormData();
formData.append('photo', this.SelectedFile, this.SelectedFile.name);
// Create options for the request
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'multipart/form-data', // <-- IMPORTANT
'Accept': 'application/json'
})
};
this.http.post(URL, formData, httpOptions).subscribe(...)
There are many ways to transfer file to the server, if it's a common scenario, then I will suggest using Dropzone.js angular wrapper.
Better have a custom angular component with all the custom configuration of Dropzone to meet your needs.
Using it is also quite easy, once you have installed the package, file input is as simple as follow:
<dropzone [config]="config" [message]="'Click or drag images here to upload'"
(error)="onUploadError($event)" (success)="onUploadSuccess($event)"></dropzone>
Documentation can be found here:
https://www.npmjs.com/package/ngx-dropzone-wrapper
You need to first instantiate your file in OnInit() before creating an instance in your OnUpload() function which you have done:
ngOnInit() {
this.form = new FormGroup({
photo: new FormControl(null, {
validators: [Validators.required]}) }
You can remove the validators if you like, you can also edit your onFileSelected() function like this:
this.SelectedFile = event.target.files[0] as File;
this.form.patchValue({ photo: file });
this.form.get('photo').updateValueAndValidity();
const reader = new FileReader();
reader.readAsDataURL(file);
console.log(file);
call your function like this:
onUploadFile(event: Event){}

Accessing Upload Data in React From Multer Node Server

So I figured out how to upload files using React and Node. It appears to be working, but I'm still very new to this stuff, and I can't quite understand how to access the file I just uploaded in React. I want to have it so that you use a form in React to upload a zip file, and then I want to have some scripts unzip the uploaded file and perform some actions on the contents. I have my files uploading properly, but I'm not sure how to pass the filename data into React after upload..
My server file:
const port = process.env.PORT || 5000;
const express = require('express');
const bodyParser = require('body-parser');
const multer = require('multer');
const uuidv4 = require('uuid/v4');
const path = require('path');
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, './src/uploads');
},
filename: (req, file, cb) => {
const newFilename = `${uuidv4()}${path.extname(file.originalname)}`;
cb(null, newFilename);
},
});
var fileFilter = function (req, file, cb) {
if (
file.mimetype !== 'application/zip'
) {
req.fileValidationError = 'goes wrong on the mimetype';
return cb(new Error('mimetype does not match application/zip. upload rejected'));
}
console.log('>> fileFilter good = ',file.mimetype)
cb(null, true);
}
const upload = multer({ storage: storage, fileFilter: fileFilter });
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.post('/', upload.single('selectedFile'), (req, res) => {
res.send();
});
app.listen(port, () => console.log(`Server listening on port ${port}`));
My React File:
import React, { Component } from 'react';
import axios from 'axios';
class UserForm extends Component {
constructor() {
super();
this.state = {
description: '',
selectedFile: '',
};
}
onChange = (e) => {
switch (e.target.name) {
case 'selectedFile':
this.setState({ selectedFile: e.target.files[0] });
break;
default:
this.setState({ [e.target.name]: e.target.value });
}
}
onSubmit = (e) => {
e.preventDefault();
const { description, selectedFile } = this.state;
let formData = new FormData();
formData.append('description', description);
formData.append('selectedFile', selectedFile);
console.log('form data ',formData)
axios.post('/', formData)
.then((result) => {
console.log('>> (onSubmit) file upload result = ',result);
// access results...
})
.catch(function (error) {
console.log('>> ERROR FILE UPLAOD ',error);
alert('File upload failed. Please ensure you are uploading a .zip file only')
})
}
render() {
const { description, selectedFile } = this.state;
return (
<form onSubmit={this.onSubmit}>
<input
type="text"
name="description"
value={description}
onChange={this.onChange}
/>
<input
type="file"
name="selectedFile"
onChange={this.onChange}
/>
<button type="submit">Submit</button>
</form>
);
}
}
export default UserForm;
In your case you are uploading a single file. So you need to return it from your / route like this
app.post('/', upload.single('selectedFile'), (req, res) => {
res.send( req.file );
});
When you use Multer and upload a file like this, then req.file will be your uploaded file here, which is selectedFile. So, you need to return it to use where ever you want.
This req.file has some information like originalname, filename, path and so on. You can use those information on your front-end. For example, you can grab path (which is the full path of the uploaded file) then use it in an <img> element.
Specifically for your situation, you can hold an imagePath state:
this.state = {
description: '',
selectedFile: '',
imagePath: '',
};
Then update your state within your axois' .then method:
axios.post('/', formData)
.then((result) => {
this.setState({imagePath: result.data.path})
})
....
and use it in your component:
{this.state.imagePath && <img src={this.state.imagePath} /> }
This is a very simple example, logic can be more complex when your app gets bigger.

Resources