i have a problem if you can help,
i've deployed my app on heroku and it's working properly on localhost when i upload product picture when launching on localhost everything is working and the picture is added to my public folder and the url is passed to my Product image url field, but when doing the same on heroku deployed app it shows: "ENOENT: no such file or directory, open 'public/obNHUnF85G2kHtApYvix5D-1200-80-1617359184714.png"
thx in advance
Here is my app.js
const express = require('express')
const morgan = require('morgan')
const mongoose = require('mongoose')
const jwt = require('jsonwebtoken');
var cors = require('cors')
const errorHandler = require('./middleware/error')
const app = express()
var corsOptions = {
origin: "*",
optionsSuccessStatus: 200 // some legacy browsers (IE11, various SmartTVs) choke on 204
}
app.use(cors(corsOptions))
app.use(express.static('public'))
app.use(express.json())
app.use(morgan('tiny'))
require('dotenv/config')
const productRouter = require('./Routes/ProductRouter')
const categoryRouter = require('./Routes/CategoryRouter');
const authRoute = require('./Routes/auth');
const userRoute = require('./Routes/user');
const OrderRouter = require('./Routes/OrderRouter');
const imagesRouter = require('./Routes/addImagesRoute');
const { static } = require('express');
const apiUrl = process.env.API_URL
const port = process.env.PORT;
mongoose
.connect(process.env.CONNECTION_STRING, {
useUnifiedTopology: true,
useNewUrlParser: true
})
.then(() => {
console.log('connected successfully')
})
.catch(err => {
console.log(err)
})
app.use(`${apiUrl}/products`, productRouter)
app.use(`${apiUrl}/categories`, categoryRouter)
app.use(`${apiUrl}/auth`, authRoute)
app.use(`${apiUrl}/users`, userRoute)
app.use(`${apiUrl}/orders`, OrderRouter)
app.use(`${apiUrl}/images`, imagesRouter)
app.use(errorHandler);
app.listen(port, () => console.log(`Example app listening on port ${port}!`))
My Uploading Router
const express = require('express')
const { uploadImage, uploadGalleryImages } = require('../controllers/uploadController')
const { authorize, protect } = require('../middleware/auth')
const uploadRouter = express.Router()
const multer = require('multer')
const path = require('path')
const FILE_TYPE_MAP = {
'image/png': 'png',
'image/jpeg': 'jpeg',
'image/jpg': 'jpg'
}
const storage = multer.diskStorage({
destination: function(req, file, cb) {
const isValid = FILE_TYPE_MAP[file.mimetype]
let uploadError = new Error('invalid image type')
if (isValid) {
uploadError = null
}
cb(uploadError, 'public/')
},
filename: function(req, file, cb) {
const fileName = file.originalname.split(' ').join('-')
const extension = FILE_TYPE_MAP[file.mimetype]
cb(null, `${fileName.split('.', 1)}-${Date.now()}${path.extname(file.originalname)}`)
}
})
const uploadOptions = multer({ storage: storage })
uploadRouter.use(protect)
uploadRouter.use(authorize('publisher', 'admin'))
uploadRouter.post(
'/:productId/image',
uploadOptions.single('image'),
uploadImage
);
uploadRouter.put('/:productId/images', uploadOptions.array('images', 8), uploadGalleryImages);
module.exports = uploadRouter;
Adding url and hash function
const file = req.file
if (!file) return res.status(400).send('No image in the request')
const fileName = file.filename
const basePath = `${req.protocol}://${req.get('host')}/`
try {
const product = await Product.findByIdAndUpdate(
req.params.productId, {
imageUrl: `${basePath}${file.filename.split('.', 1)}${path.extname(
file.originalname
)}`
}, { new: true }
);
await product.save();
const url = product.imageUrl;
const hash = await handler(url.toString());
const hashString = JSON.parse(hash).blurHash;
await Product.findByIdAndUpdate(req.params.productId, {
imageHash: hashString
}, { new: true })
product.save()
res.send(product)
} catch (error) {
return next(new ErrorResponse(`an error occured : ${error}`, 400))
}
})```
when i upload on localhost [Local host request and result][1]
when i upload on heroku server [Heroku][2]
[1]: https://i.stack.imgur.com/Fp8OK.png
[2]: https://i.stack.imgur.com/oaFuC.png
Related
I am trying to upload a file to the API, sort the numbers and then return the result in another text file that is available to download. I upload the file, and when I start the calculation I get the Internal Server Error. The API is running on port 3000 and I start the React App.js on port 3001.
Is there something I'm doing wrong?
This is the API's app.js:
const express = require('express');
const multer = require('multer');
const bodyParser = require('body-parser');
const fs = require('fs');
const app = express();
const storage = multer.memoryStorage();
const upload = multer({ storage: storage });
app.use(bodyParser.text({ type: 'text/plain' }));
app.post('/sort', upload.single('inputFile'), (req, res) => {
console.log(req.file)
const input = req.file.buffer.toString().split('\n').map(Number);
const result = input.sort((a, b) => b - a);
const resultText = result.join('\n');
fs.writeFile('result.txt', resultText, (err) => {
if(err) throw err;
res.send('File succesfully sorted!');
});
res.set('Content-Type', 'text/plain');
res.send(resultText);
});
app.listen(3000, () => {
console.log('API is listening on port 3000');
});
This is the React App.js:
const [inputFile, setInputFile] = useState(null);
const [result, setResult] = useState(null);
const [processingTime, setProcessingTime] = useState(null);
const handleFileUpload = (event) => {
setInputFile(event.target.files[0]);
};
const startCalculation = async (event) => {
event.preventDefault();
const startTime = performance.now();
const formData = new FormData();
formData.append('inputFile', inputFile);
console.log(inputFile)
const response = await fetch("http://localhost:3000/sort", {
method: 'POST',
body: formData,
mode: 'no-cors',
});
const data = await response.text();
console.log(data);
setResult(data);
setProcessingTime(performance.now() - startTime);
};
const handleDownload = (event) => {
event.preventDefault();
const file = new Blob([result], {
type: 'text/plain'
});
const fileURL = URL.createObjectURL(file);
const link = document.createElement('a');
link.href = fileURL;
link.download = 'result.txt';
link.click();
};
The issue is on the client you are setting the input name to inputFile, however, on the backend you are telling Multer that the input name is myFile.
Change from this:
upload.single("myFile")
To this:
upload.single("inputFile")
I am trying to access req.body in express middleware in a router, however it is consoling empty req.body. However in actual data does exist. I am already using body-parser at app level but it is not working for me. Below is my code, I am trying to access req.body in authorization middleware in categories.js file
index.js
const express = require('express');
const bodyParser = require('body-parser');
//Importing Routers
const customersRouter = require('./routes/customers');
const categoriesRouter = require('./routes/categories');
const itemsRouter = require('./routes/items');
const usersRouter = require('./routes/users');
const tablesRouter = require('./routes/tables');
const ridersRouter = require('./routes/riders');
const taxtypesRouter = require('./routes/taxtypes');
const branchesRouter = require('./routes/branches');
const subscribeRouter = require('./routes/subscription');
const loginRouter = require('./routes/login');
//Importing Database Connection
//const db = require('./dbConnection');
const app = express();
app.use(bodyParser.urlencoded({extended:false}));
app.use(bodyParser.json());
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Server is listening at port ${PORT}`));
app.use('/subscribe', subscribeRouter);
app.use('/login', loginRouter);
app.use('/client/:clientID/user/:userID/customers', customersRouter);
app.use('/:clientID/categories', categoriesRouter);
app.use('/client/:clientID/user/:userID/items', itemsRouter);
app.use('/client/:clientID/user/:userID/users', usersRouter);
app.use('/client/:clientID/user/:userID/tables', tablesRouter);
app.use('/client/:clientID/user/:userID/riders', ridersRouter);
app.use('/client/:clientID/user/:userID/taxtypes', taxtypesRouter);
app.use('/client/:clientID/user/:userID/branches', branchesRouter);
router file categories.js
const express = require('express');
const util = require('util');
const categoriesRouter = express.Router();
const verifyToken = require("../functions/userVarification");
const multer = require('multer');
const fs = require('fs');
var path = require('path');
//Importing Database Connection
const db = require('../dbConnection');
const query = util.promisify(db.query).bind(db);
// File Uploading through Multer
var storage = multer.diskStorage({
destination: (req, file, cb) => {
const path = `./uploads/${req.body.clientID}/categories`;
fs.mkdirSync(path, { recursive: true });
return cb(null, path);
},
filename: function (req, file, cb) {
cb(null, req.body.clientID + "_" + "Category_" + file.originalname.replace(".", "_") +"_" + Date.now() + path.extname(file.originalname)) //Appending extension
}
})
var upload = multer({ storage: storage });
function authorization(req, res, next) {
//Only Admins are allowed to add category
const roleID = parseInt(req.authData.roleID);
if (roleID !== 1) res.status(403).json({ msg: "Sorry your are not authorized to add categories" });
else {
next();
}
}
categoriesRouter.post('/', verifyToken, (req, res, next) => authorization(req, res, next), upload.single('Image'), (req, res) => {
//destructuring request body
const clientID = parseInt(req.body.clientID);
const userID = parseInt(req.body.userID);
const name = req.body.Name;
const branches = req.body.Branches;
const color = req.body.Color;
let imageSource = `uploads/${req.body.clientID}/categories/${req.file.filename}`;
const imageInPOS = req.body.ImageInPOS;
const visibilityInPOS = req.body.VisibilityInPOS;
try {
(async () => {
//Create Local Category ID
const SQL1 = `SELECT COUNT(ClientID) AS 'Categories' FROM categories WHERE ClientID = ${clientID};`;
let counter = await query(SQL1);
counter = counter[0].Categories + 1;
const localCategoryID = 'CT' + ('00' + counter).slice(-3);
//SQL for Adding Category in the database
const SQL2 = `INSERT INTO categories (CategoryID, LocalCategoryID, ClientID, CategoryName, ShowInBranches, CategoryColor, ImageSrc, DisplayInPOS, DisplayImage)
VALUES (NULL, '${localCategoryID}', ${clientID}, '${name}', '${branches}', '${color}', '${imageSource}', ${visibilityInPOS}, ${imageInPOS})`;
const addedCategory = await query(SQL2);
if(addedCategory.affectedRows > 0) {
res.status(200).json({ msg : "Category have been added"});
}
else {
res.status(500).json({ msg: "Something went wrong" });
}
})()
}
catch (err) {
res.status(500).json({ msg: "Something went wrong" });
return;
}
});
module.exports = categoriesRouter;
the body-parser module don't require to explicitly install.
Its provided under the methods express.json() and express.urlencoded().
so add
app.use(express.urlencoded({extended: true}));
app.use(express.json())
and remove
require('body-parser');
app.use(bodyParser.urlencoded({extended:false}));
app.use(bodyParser.json());
I am trying to build a web application using MVC pattern, but I have a problem with POST-request to "http://localhost:5000/api/device": postman POST request attempt
The problem only occurs on POST request, GET request is OK: get request
Code:
index.js
const express = require('express')
const sequelize = require('./db')
require('dotenv').config();
const path = require('path')
const models = require('./models/models')
const cors = require("cors")
const app = express();
const router = require('./routes/index')
const fileUpload = require('express-fileupload')
const errorHandler = require('./milddleware/ErrorHandlingMiddleware')
app.use(cors())
app.use(express.json())
app.use(express.static(path.resolve(__dirname, 'static')))
app.use(fileUpload({}))
app.use('/api', router)
app.use(errorHandler)
const PORT = process.env.PORT || 5000;
const db_start = async ()=>
{
try
{
await sequelize.authenticate();
await sequelize.sync();
app.listen(PORT, ()=>{console.log(`Server started on port ${PORT}`)})
}
catch (e)
{
console.log(e);
}
}
db_start()
routes/index.js
const Router = require('express')
const router = new Router();
const deviceRouter = require('./deviceRouter')
const typeRouter = require('./typeRouter')
const brandRouter = require('./brandRouter')
const userRouter = require('./userRouter')
router.use('/user', userRouter)
router.use('/device', deviceRouter)
router.use('/brand', brandRouter)
router.use('/type', typeRouter)
module.exports = router
routes/deviceRouter.js
const Router = require('express')
const DeviceController = require('../controllers/deviceController')
const router = new Router();
router.post('/', DeviceController.create)
router.get('/', DeviceController.getAll)
router.get('/:id', DeviceController.getOne)
module.exports = router
controllers\deviceController.js
const uuid = require('uuid')
const path = require('path')
const {Device} = require('../models/models')
const ApiError = require("../errors/ApiError")
class DeviceController
{
async create(req, res, next)
{
console.log(req);
try{
const {name, price, brandId, typeId, info} = req.body;
const {img} = req.files;
let fileName = uuid.v4() + ".jpg";
img.mv(path.resolve(__dirname, '..', 'static', fileName));
const device = await Device.create({name, price, brandId, typeId, img: fileName});
return res.json(device);
}
catch(e)
{
next(ApiError.badRequest(e.message));
}
}
async getAll(req, res)
{
const {brandId, typeId} = req.query
let devices;
if(!brandId && !typeId)
{
devices = await Device.findAll()
}
if(brandId && !typeId)
{
devices = await Device.findAll({where: {brandId}})
}
if(!brandId && typeId)
{
devices = await Device.findAll({where: {typeId}})
}
if(brandId && typeId)
{
devices = await Device.findAll({where: {brandId,typeId}})
}
return res.json(devices)
}
async getOne(req,res)
{
}
}
module.exports = new DeviceController()
I logged the request and saw that the request body came up empty and req.files is undefined.
I compared these router and controller with others and found no differences in structure.
What am I doing wrong?
You cannot retrieve files of form-data directly from req.files.
Use formidable (it's an npm package) to parse form-data and work with it easily. Here are the steps:
Run npm install formidable.
Import formidable and fs package in your controller script with:
const formidable = require('formidable')
const fs = require('fs');
Change your create function with this:
async create(req, res, next) {
let form = new formidable.IncomingForm()
form.keepExtensions = true
form.parse(req, async (err, fields, files) => {
// console.log('err', err)
// console.log('fields', fields)
if (err) {
next(ApiError.badRequest(err));
return;
}
const { name, price, brandId, typeId, info } = fields; // <-- fields contain all your text data. it's like req.body
const { img } = files; // <-- it should work now!
let fileName = uuid.v4() + ".jpg";
fs.writeFileSync(path.resolve(__dirname, '..', 'static', fileName), img);
const device = await Device.create({ name, price, brandId, typeId, img: fileName });
return res.json(device);
}
}
My express server is not responding to any of the route requests, whatever I do no luck.
following are the routes I am trying to access:
http://localhost:3000/api/files
http://localhost:3000/files/<:uuid>
http://localhost:3000/api/files/send
Terminal output:
Response via insomia:
Following are the files of the project
server.js
const express = require('express');
const app = express();
const path = require('path');
app.use(express.static('public'));
app.use(express.json);
const PORT = process.env.PORT||3000;
const connectDB = require('./config/db');
connectDB();
app.set('views', path.join(__dirname, '/views'));
app.set('view engine', 'ejs');
app.use('/api/files', require('./routes/files'));
app.use('/files', require('./routes/show'));
app.use('/files/download', require('./routes/download'));
console.log("Server");
app.listen(PORT, () =>{
console.log(`Listening on port ${PORT}`);
});
routes/files.js
const router = require('express').Router();
const multer = require('multer');
const path = require('path');
const File = require('../models/file');
const {v4 : uuid4} = require('uuid');
const sendMail = require('../services/email');
require('dotenv').config();
let storage = multer.diskStorage({
destination: (req, file, cb) => cb(null, 'uploads/'),
filename: (req, file, cb) => {
const uniqueName = `${Date.now()}-${Math.round(Math.random() * 1E9)}${path.extname(file.originalname)}`;
cb(null, uniqueName)
},
});
let upload = multer({
storage:storage,
limit: { fileSize: 100000*200},
}).single('myfile');
router.post('/', (req, res) => {
console.log("Reached here");
console.log(req);
// store locally
upload(req, res, async (err) => {
// check for valid req
if(!req.file){
return res.json({
error: "Error processing file!"
});
}
if(err){ return res.status(500).send({error: err.message})}
// store in db
const file = File({
filename: req.file.filename,
uuid: uuid4(),
path: req.file.path,
size: req.file.size,
});
const response = await file.save();
return res.json({file: `${process.env['APP_BASE_URL']}/files/${response.uuid}`})
});
//return link
});
router.post('/send', async (req, res) => {
console.log(req.body);
const {uuid, emailTo, emailFrom} = req.body;
if(!uuid || !emailTo || !emailFrom){ return res.status(422).send({error: 'All files are required.'});}
const file = await File.findOne({uuid: uuid});
if(file.sender){
return res.status(422).send({error: 'Email already sent.'});
}
file.sender = emailFrom;
file.receiver = emailTo;
const response = await file.save();
sendMail({
from: emailFrom,
to: emailTo,
subject: "shareMe File Sharing",
text: `${emailFrom} shared a file with you`,
html: require('../service/template')({
emailFrom: emailFrom,
downloadLink: `${process.env['APP_BASE_URL']}/files/${file.uuid}`,
size: parseInt(file.size/1000)+'KB',
expires: '24 hours'
})
});
return res.send({success: true});
});
module.exports=router;
routes/show.js
const router = require('express').Router();
const File = require('../models/file');
router.get('/:uuid', async (req, res) => {
try{
const file = await File.findOne({
uuid: req.params.uuid
});
if(!file){
return res.render('download', {error: 'File not fetched.'});
}
console.log(`${process.env['APP_BASE_URL']}/files/download/${file.uuid}`);
return res.render('download', {
uuid: file.uuid,
fileName: file.filename,
size: file.size,
downloadLink: `${process.env['APP_BASE_URL']}/files/download/${file.uuid}`,
});
}catch(err){
return res.render('download', {error: 'Something seems to be upset :('});
}
});
module.exports = router;
routes/download.js
const router = require('express').Router();
const File = require('../models/file');
router.get('/:uuid', async (req, res) => {
// Extract link and get file from storage send download stream
const file = await File.findOne({ uuid: req.params.uuid });
// Link expired
if(!file) {
return res.render('download', { error: 'Link has been expired.'});
}
const response = await file.save();
const filePath = `${__dirname}/../${file.path}`;
res.download(filePath);
});
module.exports = router;
Instead of storing file directly into mongodb I want to store using mongoose library. How to use mongoose library using the code frame same?
Update:: Saved in mongoose library. Now it uploads all of the file but how can I just store only the docx file instead of image file. And uploading anyother file should say invalid file.
update 2:: Resolved with the issue. Now capable of only allowing docxx file. Now need to add filename with extention as mentioned in the comment section. How to use that part of code.
Middleware::upload.js
const util = require("util");
const multer = require("multer");
const GridFsStorage = require("multer-gridfs-storage");
const mongoose = require('mongoose');
const Grid = require('gridfs-stream');
const mongoURI ='mongodb://localhost:27017/file_uploaded';
const promise = mongoose.connect(mongoURI, { useNewUrlParser: true });
const conn = mongoose.connection;
let gfs;
conn.once('open',() => {
gfs = Grid(conn, mongoose.mongo);
gfs.collection('uploads');
});
var storage = new GridFsStorage({
db: promise,
options: { useNewUrlParser: true, useUnifiedTopology: true },
file: (req, file) => {
return new Promise((resolve, reject) => {
if (file.mimetype === 'application/vnd.openxmlformats-
officedocument.wordprocessingml.document') {
resolve({
bucketName: 'words'
})
***//Add filename inorder to maintain original final name with
extention***
//const filename = `${Date.now()}-
bezkoder-${file.originalname}`;
//return filename;
} else {
reject(Error("File type has been rejected"));
}
});
}
});
var uploadFile = multer({ storage: storage }).single("file");
var uploadFilesMiddleware = util.promisify(uploadFile);
module.exports = uploadFilesMiddleware;
You dont need to use GridFsStorage. Try below code.
// file-controller.js
const express = require('express');
const mongoose = require('mongoose');
const multer = require('multer');
const router = express.Router();
const multerStorage = multer.memoryStorage();
const upload = multer({ storage: multerStorage, });
const imageSchema = mongoose.Schema({
image: { data: Buffer, contentType: String },
}, { timestamps: true });
const ImageModel = mongoose.model('images', imageSchema);
router.post('/upload', upload.single('image'), async (req, res, next) => {
const image = { data: new Buffer.from(req.file.buffer, 'base64'), contentType: req.file.mimetype }
const savedImage = await ImageModel.create(image);
res.send(savedImage);
});
router.get('/getImage/:id', async (req, res, next) => {
const { id: _id } = req.params;
// If you dont use lean(), you wont decode image as base64
const image = await ImageModel.findOne({ _id }).lean().exec();
res.send(image);
});
module.exports = router;
Serve route via express.
const express = require('express');
const FileController = require('./file-controller');
const app = express();
// Mongo DB connection code ...
// Serve static files code ...
app.use('/file', FileController);
app.listen(3000, () => console.log(`Example app listening on port ${port}!`));