I am getting this error on Postman when I send request for uploading files on Feathersjs:
{
"name": "GeneralError",
"message": "ENOENT: no such file or directory, open 'public/uploads/pic'",
"code": 500,
"className": "general-error",
"data": {},
"errors": {}
}
My uploads.service.js:
const {Uploads} = require('./uploads.class');
const createModel = require('../../models/uploads.model');
const hooks = require('./uploads.hooks');
const multer = require('multer');
const storage = multer.diskStorage({
destination: (_req, _file, cb) => cb(null, 'public/uploads'), // where the files are being stored
filename: (_req, file, cb) => {
console.log(_req.body);
//cb(null, ${_req.body.name});
cb(null, `${_req.body.name}`); //
}, // getting the file name
});
const uploads = multer({
storage,
limits: {
fieldSize: 1e8,
fileSize: 1e7,
},
});
module.exports = function(app) {
const options = {
Model: createModel(app),
paginate: app.get('paginate'),
multi: true,
};
// Initialize our service with any options it requires
app.use(
'/uploads',
uploads.array('files'),
(req, _res, next) => {
const {method} = req;
if (method === 'POST' || method === 'PATCH') {
console.log(req.files);
console.log(req.body);
req.feathers.files = req.body.files;
const body = [];
for (const file of req.files)
body.push({
name: req.body.name,
newNameWithPath: file.path,
});
req.body = method === 'POST' ? body : body[0];
}
next();
},
new Uploads(options, app),
);
// Get our initialized service so that we can register hooks
const service = app.service('uploads');
service.hooks(hooks);
};
This is my uploads.model.js:
module.exports = function(app) {
const modelName = 'uploads';
const mongooseClient = app.get('mongooseClient');
const {Schema} = mongooseClient;
const schema = new Schema(
{
name: {type: String, required: true},
},
{
timestamps: true,
},
);
// This is necessary to avoid model compilation errors in watch mode
// see https://mongoosejs.com/docs/api/connection.html#connection_Connection-deleteModel
if (mongooseClient.modelNames().includes(modelName)) {
mongooseClient.deleteModel(modelName);
}
return mongooseClient.model(modelName, schema);
};
I really cannot figure out where exactly the problem is. According to me it is supposed to make the folder automatically when I upload the file.
I would really appreciate some help. Thank you in advance.
It was my own mistake. I made the uploads folder inside public folder myself and now it's working.
Related
I currently use multer middleware like below
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "public");
},
filename: function (req, file, cb) {
cb(null, req.params.id + "_" + file.originalname);
},
});
export const multerUploadSingle = (req: Request, res: Response, next: NextFunction) => {
const upload = multer({ storage: storage }).single("file");
upload(req, res, (error: unknown) => {
if (error instanceof multer.MulterError) {
const message = `file upload fail: ${error.message}`;
next(new HttpException(message, HttpStatus.BadRequest));
} else if (error instanceof Error) {
const message = `file upload fail: ${error.message}`;
next(new HttpException(message, HttpStatus.InternalServerError));
} else {
// upload success
next();
}
});
}
and use in router like this
FileRouter.post("/upload/:id", multerUploadSingle, (req, res) => {...});
However, I felt I want to refactor this middleware in class, and rewrote the code like this,
export class Multer {
private readonly storage: multer.StorageEngine;
constructor() {
this.storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "public");
},
filename: function (req, file, cb) {
cb(null, req.params.id + "_" + file.originalname);
},
});
}
uploadSingle(req: Request, res: Response, next: NextFunction) {
const upload = multer({ storage: this.storage }).single("file");
upload(req, res, (error: unknown) => {
if (error instanceof multer.MulterError) {
const message = `file upload fail: ${error.message}`;
next(new HttpException(message, HttpStatus.BadRequest));
} else if (error instanceof Error) {
const message = `file upload fail: ${error.message}`;
next(new HttpException(message, HttpStatus.InternalServerError));
} else {
// upload success
next();
}
});
}
}
const multer = new Multer();
FileRouter.post("/upload/:id", multer.uploadSingle, (req, res) => {...});
With my short knowledge, I think both case should have the same result, but the latter case which uses class made middleware doesn't work at all. It's seems method "uploadSingle" is never called, thus multer not uploading the file.
Did I make any mistake with class usage? or is it just express can only use function defined middleware?
Your code should follow the MVC pattern.
You can do stuff like this:
routerFile.js
const upload = require("../../configs/multer");
const postController = require("../../controllers/postController");
const multiUploadEvent = upload.fields([
{ name: "images", maxCount: 2 },
{ name: "video", maxCount: 2 }
]);
router.post("/add-event-post", multiUploadEvent, postController.addEventPost);
module.exports = router;
multer.js
const multer = require('multer');
const multerFilter = (req, file, cb) => {
console.log("Mime type :", file.mimetype.split('/')[0]);
if (file.mimetype.split('/')[0] === 'image' || file.mimetype.split('/')[0] === 'video' || file.mimetype.split('/')[0] === 'audio') {
cb(null, true);
} else {
cb(new Error('Please upload img, audio, or video file only.'), false);
}
};
const storage = multer.memoryStorage();
const upload = multer({
storage: storage,
fileFilter: multerFilter,
limits: {
fileSize: , 50 * 1024 * 1024// 50 Mb
},
});
module.exports = upload;
postController.js
const addEventPost = async (request, response) => {
try {
let { title, ..... } = request.body;
const images = request.files.images;
const video = request.files.video;
console.log(title);
console.log(images);
console.log(videos);
//upload to services likes aws and save to database
.
.
.
return response
.status(200)
.json({
message: "Event post added successfully"
});
} catch (error) {
console.log(error);
response.status(500).json({
error: "Something went wrong",
});
}
}
Im creating a MERN stack application and in the react front end, i intend to have a form to add a product, the form is going to have a lot of inputs including an image upload option. I want to know how to handle the image upload from the express side using Multer. i have used their documentation but im not sure whether the code i wrote is correct. I also haven't created the front end yet, so i am currently using postman to test the api. How do i test whether the image upload functionality is working using postman? I would be posting the code i have written so far for context.
Product model:
const mongoose = require('mongoose')
const ProductSchema = new mongoose.Schema({
name:{
type: String,
required: [true, 'please provide a product name'],
maxlength: 20,
minlength: 3
},
category: {
type: String,
required: [true, 'please provide a category'],
maxlength: 20,
minlength: 3
},
quantity: {
type: Number,
required: [true, 'please provide the quantity']
},
price: {
type: Number,
required: [true, 'please provide the price']
},
description: {
type: String,
required: [true, 'please provide the description'],
trim: true
},
image: {
type: String
},
createdBy: {
type: mongoose.Types.ObjectId,
ref: 'User',
required: [true, 'Please provide the user'],
}, },
{ timestamps: true } )
module.exports = mongoose.model('Product', ProductSchema)
file upload.js:
const multer = require('multer')
const { v4: uuidv4 } = require('uuid')
const storage = multer.diskStorage({
destination: function(req, file, cb) {
cb(null, '../uploads')
},
filename: function(req, file, cb){
cb(null, uuidv4() + '-' + Date.now() + path.extname(file.originalname) )
}
})
const fileFilter = (req, file, cb) => {
const allowedTypes = ['image/jpeg', 'image/jpg', 'image/png']
if(allowedTypes.includes(file.mimetype)) {
cb(null, true)
}else(
cb(null, false)
)
}
const upload = multer({storage, fileFilter})
module.exports = upload
Product router:
const express = require('express')
const router = express.Router()
const upload = require('../utils/fileUpload')
const {getAllProducts, createProduct, getProduct, updateProduct, deleteProduct} = require('../controllers/products')
router.route('/').post(upload.single('image'), createProduct).get(getAllProducts)
router.route('/:id').get(getProduct).patch(updateProduct).delete(deleteProduct)
module.exports = router
Product controller:
const Product = require('../models/Product')
const { StatusCodes } = require('http-status-codes')
const { BadRequestError, NotFoundError } = require('../errors')
const createProduct = async (req, res) => {
req.body.createdBy = req.user.userId
const product = await Product.create({...req.body, image: req.file})
res.send('create Product')
}
const getAllProducts = async (req, res) => {
res.send('get All products')
}
const getProduct = async (req, res) => {
res.send('get product')
}
const updateProduct = async (req, res) => {
res.send('update product')
}
const deleteProduct = async (req, res) => {
res.send('delete product')
}
module.exports = {
getAllProducts, createProduct, getProduct, updateProduct, deleteProduct
}
You can change the parameter type to file from postman to try uploading files when sending a request :
I apologize for something that could be very simple to fix, I just cannot figure it out. I've used this code in another project before with the exact same layout and that project works, but on this project, it's not working.
As seen below in the code, I am doing a simple findAll() query. The database is connected perfectly fine but I keep getting this error no matter what I try.
[Error is here][1]
I wanted to double-check to see if it's because of me or if it's an issue with the latest release of sequelize, but I doubt it because I haven't seen any issues like this pop up yet.
But if nobody can see an issue, I will submit a bug ticket.
root/routes/ShopItems.js
const express = require('express')
const router = express.Router()
const { ShopItems } = require('../models')
router.get('/', async(req, res) => {
const data = await ShopItems.findAll()
res.json(data)
})
module.exports = router
root/index.js
const express = require('express')
const session = require('express-session')
const app = express()
const cors = require('cors')
const db = require('./models')
const { SECRET, PORT } = require('./temp_secret')
app.use(express.json())
app.use(cors())
app.use(session({
secret: SECRET,
resave: true,
saveUninitialized: false,
}))
app.use(express.urlencoded({extended: false}))
app.all('/*', function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*")
res.header("Access-Control-Allow-Headers", "X-Requested-With,GET,POST,content-type,Origin,Accept")
req.header("Access-Control-Allow-Origin", "*")
req.header("Access-Control-Allow-Headers", "X-Requested-With,GET,POST,content-type,Origin,Accept")
next()
})
// Routers
const ShopItemsRouter = require('./routes/ShopItems')
app.use('/shop', ShopItemsRouter)
db.sequelize.sync().then(() => {
app.listen(PORT, () => {
console.log(`Running on ${PORT}`)
})
})
root/models/index.js
const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const basename = path.basename(__filename);
const env = process.env.NODE_ENV || 'development';
const config = require(__dirname + '/../config/config.json')[env];
const db = {};
let sequelize;
if (config.use_env_variable) {
sequelize = new Sequelize(process.env[config.use_env_variable], config);
} else {
sequelize = new Sequelize(config.database, config.username, config.password, config);
}
fs
.readdirSync(__dirname)
.filter(file => {
return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
})
.forEach(file => {
const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes);
db[model.name] = model;
});
Object.keys(db).forEach(modelName => {
if (db[modelName].associate) {
db[modelName].associate(db);
}
});
db.sequelize = sequelize;
db.Sequelize = Sequelize;
module.exports = db;
root/models/ShopItems.js
module.exports = (sequelize, DataTypes) => {
const ShopItems = sequelize.define('shop_items', {
item_name: {
type: DataTypes.STRING(),
allowNull: false
},
item_description: {
type: DataTypes.STRING(),
allowNull: false
},
item_type: {
type: DataTypes.STRING(),
allowNull: false,
defaultValue: 'Single Item'
},
item_categories: {
type: DataTypes.JSON(),
allowNull: false,
defaultValue: '{}'
},
item_genres: {
type: DataTypes.JSON(),
allowNull: false,
defaultValue: '{}'
},
initial_item_price: {
type: DataTypes.INTEGER(),
allowNull: false,
defaultValue: 0
},
item_discount: {
type: DataTypes.INTEGER(),
allowNull: false,
defaultValue: 0
},
item_price: {
type: DataTypes.INTEGER(),
allowNull: false,
defaultValue: 0
},
number_of_sales: {
type: DataTypes.INTEGER(),
allowNull: false,
defaultValue: 0
}
})
return ShopItems
}
Thank you anyone that helps, I greatly appreciate it.
[1]: https://i.stack.imgur.com/zBOTE.png
In the ShopItems routes file you have
const { ShopItems } = require('../models')
The problem with this is that the index.js file (Which is what gets brought in with the above statement), does not export a ShopItems property. This is why your error message is saying "Cannot read properties of undefined", because in this case the ShopItems variable is returning undefined.
Without seeing your ShopItems model I can't say for sure if this will work, but if that model exists and is set up properly, you should be able to do:
const db = require('../models')
And then inside of the route method:
const data = await db.ShopItems.findAll()
I am trying to host a NextJS app and everything seems to be working fine locally. I am able to get the data from the site and I can go to the site and see the raw json that is being returned, but when I try to get things working on production the API is completely inaccessible through the browser and through the Axios requests.
The server just returns 500 or Internal Server Error.
I have tried deploying on DigitalOcean App Platform and AWS Amplify, but both fail to connect to the API routes.
I followed this tutorial for the NextJS SSR method that says to build and start using
// next.config.js
const path = require('path')
const Dotenv = require('dotenv-webpack')
require('dotenv').config()
module.exports = {
webpack: (config) => {
config.plugins = config.plugins || []
config.module.rules.push({
test: /\.svg$/,
use: ["#svgr/webpack"]
});
config.plugins = [
...config.plugins,
// Read the .env file
new Dotenv({
path: path.join(__dirname, '.env'),
systemvars: true
})
]
return config
},
sassOptions: {
includePaths: [path.join(__dirname, 'styles')]
}
}
// package.json
...
"scripts": {
"dev": "next dev",
"build": "next build",
"digitalocean": "next start -H 0.0.0.0 -p ${PORT:-8080}",
"start": "next start"
},
...
// api.js
const axios = require('axios')
const {getS3URL} = require('./aws')
require('dotenv').config()
export default async (req, res) => {
const config = {
bucket: 'bucket',
key: 'folder/data.json'
}
const request = await axios.get(await getS3URL(config));
try {
res.status(200).json(JSON.stringify(request.data))
} catch {
res.status(500).json({ error: '500', response })
res.status(400).json({ error: '400', response })
}
}
// frontend.js
...
const getData = async () => {
console.log(`${host}api/daily-trip-stats`)
const trips = await axios.get(`${host}api/daily-trip-stats`)
const routes = await axios.get(`${host}api/daily-route-stats`)
const stops = await axios.get(`${host}api/daily-stops-routes`)
const cleanUp = async (data) => {
return await data.map(fea => fea.properties)
}
return {
routes: await cleanUp(routes.data.features),
trips: await cleanUp(trips.data.features),
stops: await cleanUp(stops.data.features)
}
};
...
Checked the server logs and found that the default region was not being set properly.
var { S3Client, GetObjectCommand, Config} = require('#aws-sdk/client-s3');
import { getSignedUrl } from "#aws-sdk/s3-request-presigner";
const getS3URL = async ({bucket, key}) => {
const client = new S3Client({
region: 'us-east-1' // !!! FORGOT TO SET THE DEFAULT REGION
})
var params = {
Bucket: bucket,
Key: key,
Expires: 60,
ContentType: 'blob'
};
const s3Data = new GetObjectCommand(params);
const url = await getSignedUrl(client, s3Data, { expiresIn: 3600 });
return url
};
module.exports = {getS3URL}
When I update using raw JSON, it's working but when I use the form data it is not updating. the request body when using form data is an empty object. Why is this happening?
Here's my update code:
exports.updateProgram = catchAsync(async (req, res, next) => {
console.log('req ko body',req.body)
let doc = await Program.findByIdAndUpdate(req.params.id, req.body, { runValidators: true, new: true })
if (!doc) {
return next(new AppError('No document found with that ID', 404))
}
res.status(200).json({
status: 'success!',
data: { doc }
})
})
In Postman:
I am using multer, I actually pass the photo in req.body. Here's the code:
let multerStorage = multer.memoryStorage()
let multerFilter = (req, file, cb) => {
if (file.mimetype.split('/')[0] == 'image') {
cb(null, true)
} else {
cb(new AppError('Not an image!', 400), false)
}
}
let upload = multer({
storage: multerStorage,
fileFilter: multerFilter
})
exports.uploadPhotos = upload.fields([
{ name: 'abcd', maxCount: 10 },
{ name: 'photos', maxCount: 10 },
{name: 'photos3', maxCount: 10}
])
exports.resizePhotos = catchAsync(async (req, res, next) => {
// if (!req.files.photos || !req.files.abcd) return next()
if(req.files.abcd) {
req.body.abcd = []
await Promise.all(req.files.abcd.map(async (file, i) => {
let filename = `tour-${Date.now()}-${i + 1}.jpeg`
await sharp(file.buffer)
.resize(500,500)
.toFormat('jpeg')
.jpeg({ quality: 90 })
.toFile(`public/img/arpit/${filename}`)
req.body.abcd.push(filename)
})
)} else if(req.files.photos3) {
req.body.photos3 = []
await Promise.all(req.files.photos3.map(async (file, i) => {
let filename = `tour-${Date.now()}-${i + 1}.jpeg`
await sharp(file.buffer)
.resize(500,500)
.toFormat('jpeg')
.jpeg({ quality: 90 })
.toFile(`public/img/arpit/${filename}`)
req.body.photos3.push(filename)
})
)}
else if(req.files.photos) {
// console.log('codee here')
// } else if(req.body.photos) {
req.body.photos = []
console.log('req.files>>>', req.files)
await Promise.all(req.files.photos.map(async (file, i) => {
let filename = `tour-${Date.now()}-${i + 1}.jpeg`
await sharp(file.buffer)
.resize(500,500)
.toFormat('jpeg')
.jpeg({ quality: 90 })
.toFile(`public/img/programs/${filename}`)
req.body.photos.push(filename)
})
)
}
return next()
})
I'm importing in the routes file
Express (bodyParser) can't handle multipart form-data and that's why your code isn't working.
Take a look at multer, an express package. It is a middleware which provides the functionality you're looking for.
var cpUpload = upload.fields([{ name: 'avatar', maxCount: 1 }, { name: 'gallery', maxCount: 8 }]);
app.post('/cool-profile', cpUpload, function (req, res, next) {
// req.files is an object (String -> Array) where fieldname is the key, and the value is array of files
//
// e.g.
// req.files['avatar'][0] -> File
// req.files['gallery'] -> Array
//
// req.body will contain the text fields, if there were any
})
This might be help you. Quoted from https://www.npmjs.com/package/multer#readme