I hope that everyone is doing well. I have been attempting to do my own thing and research a way to do this for the past two or three days, but I have had no luck. Is there a way for me to create a directory after some information is inputted by the user and change to that directory to upload the files there? What I am trying to do is upload files to a folder that will be named based on the information put in by the user before multer uploads the file. If there is an alternative way, I am open to advice. My code below and I have uploaded my react js code just incase.
Node JS:
const express = require('express');
const router = express.Router();
const db = require('../config/db');
const bcrypt = require('bcrypt');
const saltRounds = 10;
const session = require('express-session');
const bodyParser = require('body-parser');
const app = express();
const SqlDbStore = require('express-mysql-session')(session);
const passport = require('passport');
const cookieParser = require('cookie-parser');
const multer = require('multer');
const path = require('path');
//----------------------------------------- BEGINNING OF PASSPORT MIDDLEWARE AND SETUP ---------------------------------------------------
app.use(cookieParser());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(passport.session());
app.use(session({
key: 'session_cookie_name',
secret: 'session_cookie_secret',
store: new SqlDbStore({
host: 'localhost',
port: 3306,
user: 'root',
password: '**************',
database: '**************',
}),
resave: false,
saveUninitialized: false,
cookie:{
maxAge:1000*60*60*24,
secure: false
}
}));
const multerDestination = './routes/Images/';
const storage = multer.diskStorage({
destination: function (req, files, cb) {
cb(null, multerDestination);
},
filename: function (req, files, cb) {
cb(null, Date.now() + '-' + files.originalname );
}
});
const upload = multer({ storage: storage }).array('files');
//----------------------------------------- END OF PASSPORT MIDDLEWARE AND SETUP ---------------------------------------------------
router.post('/properties_upload', (req, res) => {
const address = req.body.addressLine;
const address2 = req.body.addressLine2;
const city = req.body.city;
const state = req.body.state;
const addressZipCode = req.body.addressZipCode;
const mlsID = req.body.mlsID;
const rentalID = Math.floor(Math.random()*90000) + 10000;
var directory;
upload(req, res, function (err) {
if (err instanceof multer.MulterError) {
return res.status(500).json(err)
} else if (err) {
return res.status(500).json(err)
}
for (const file of req.files) {
directory = path.dirname(file.path);
}
console.log('File path: ' + directory);
return res.status(200).send(req.files);
});
});
module.exports = router;
React JS:
import React, { useEffect, useState } from 'react';
import './Properties_Upload.css';
import Navbar from '../../../Components/Navbar/Navbar';
import Footer from '../../../Components/Footer/Footer';
import Axios from 'axios';
import { useNavigate } from 'react-router-dom';
export default function Properties_Upload() {
const [addressLine, setAddressLine] = useState('');
const [addressLine2, setAddressLine2] = useState('');
const [addressCity, setAddressCity] = useState('');
const [addressState, setAddressState] = useState('');
const [addressZipCode, setAddressZipCode] = useState('');
const [mlsID, setMLSID] = useState('');
const [selectedFile, setSelectedFile] = useState('');
const [errorMessage, setErrorMessage] = useState('');
const navigate = useNavigate();
const propertyInfoHandler = () => {
const url = 'http://localhost:3001/property/properties_upload';
const data = new FormData()
for (var x = 0; x < selectedFile.length; x++) {
data.append('files', selectedFile[x]);
}
Axios.post(url, data, {
addressLine: addressLine,
addressLine2: addressLine2,
addressCity: addressCity,
addressState: addressState,
addressZipCode: addressZipCode,
mlsID: mlsID
})
.then((response) => {
navigate('/properties');
if (response.data.errorMessage){
setErrorMessage(response.data.message);
}
});
};
return (
<>
<Navbar />
<div className='propertiesUploadBody'>
<h1>Properties Upload</h1>
<div className='propertyInfoFormBody'>
<h1>Property Information</h1>
<p className='addressLine'> *Property address: <input name='addressLine' placeholder='Address' required autoComplete="off" onChange={(e) => setAddressLine(e.target.value)} /> </p>
<p className='addressLine2'> Apartment/Suite/Unit: <input name='addressLine2' placeholder='Apartment/Suite/Unit' autoComplete="off" onChange={(e) => setAddressLine2(e.target.value)} /> </p>
<p className='addressCity'> *Property city: <input name='addressCity' placeholder='City' required autoComplete="off" onChange={(e) => setAddressCity(e.target.value)} /> </p>
<p className='addressState'> *Property state: <input name='addressState' placeholder='State' maxLength={2} required autoComplete="off" onChange={(e) => setAddressState(e.target.value)} /> </p>
<p className='addressZipCode'> *Property zip code: <input type='number' name='addressZipCode' placeholder='Zip Code' required autoComplete="off" onChange={(e) => setAddressZipCode(e.target.value)} /> </p>
<p className='propertyMLSID'> MLS ID: <input type='number' name='propertyMLSID' placeholder='MLS ID' required autoComplete="off" onChange={(e) => setMLSID(e.target.value)} /></p>
</div>
<div className='propertyFileFormBody'>
<h1>Property Image Upload</h1>
<p> Upload Images of the property.</p>
<input className='uploadFiles' type='file' name='file' multiple required onChange={(e) => setSelectedFile(e.target.files)} />
</div>
<h2>{errorMessage}</h2>
<button className='uploadButton' onClick={propertyInfoHandler}>Upload Property</button>
</div>
<Footer />
</>
)
}
You can perform asynchronous file system operations before invoking the callback cb
in the destination
function.
const storage = multer.diskStorage({
destination: function (req, files, cb) {
var addressLine = req.body.addressLine;
var multerDestination;
// Decide what multerDestination should be based on addressLine or other user info.
fs.mkdir(multerDestination, function(err) {
if (err && err.code !== "EEXIST") cb(err);
else cb(null, multerDestination);
}
},
filename: ...
});
If you want to evaluate additional form fields beside the uploaded files (like the addressLine
), they must come before the files when you construct the FormData
:
const data = new FormData();
data.append('addressLine', addressLine);
...
for (var x = 0; x < selectedFile.length; x++) {
data.append('files', selectedFile[x]);
}
Axios.post(url, data).then(...);
Otherwise, req.body
will not yet be filled when the destination
function is executed.