I am uploading a text file using multer. I need to get the content of the text file. So, I am using buffer.toString('utf8'). But, it is giving me an error message which is as follows: Cannot read property 'toString' of undefined.Does anyone have any idea that why is it happening? Also, I have checked that req.file is not undefined
My code is as follows:
const multer = require('multer');
const upload = multer({ dest: 'uploads/' });
function setupRoutes(app) {
//#TODO add appropriate routes
const base = app.locals.base;
// app.get(`${base}/search.html`, doSearch(app));
app.get(`${base}/add.html`, createDocsForm(app));
app.post(`${base}/add.html`, upload.single('file'), doAdd(app));
app.get(`${base}/:id`, showContent(app));// must be last
}
function doAdd(app){
return async function(req, res) {
try{
//console.log(req.file.buffer.toString(`utf8`));
// if(req.file === undefined) console.log("req is undefined");
const fileContent = req.file.buffer.toString('utf8');
// const fileContent = req.file;
let fileName = req.file.originalname;
fileName = fileName.substring(0, fileName.lastIndexOf('.'));
const obj = {
name: fileName,
content: fileContent
}
const a = await app.locals.model.uploadDoc(obj);
res.redirect(`${app.locals.base}/${fileName}`);
}catch(err){
if(req.file == undefined){
const err = "Select file to upload";
const model = {base:app.locals.base, err:err};
const html = doMustache(app,'add',model);
res.send(html);
}
else{
const errors= wsErrors(err);
const model = {base:app.locals.base, errors:errors};
const html = doMustache(app,'add',model);
res.send(html);
}
} }
}
You have conflicting code:
const upload = multer({ dest: 'uploads/' });
If you want file.buffer, you'll need to import without dest like:
const upload = multer({});
Your import implies DiskStorage, but multer only defines buffer on files using MemoryStorage:
function Multer (options) {
if (options.storage) {
this.storage = options.storage
} else if (options.dest) {
this.storage = diskStorage({ destination: options.dest })
} else {
this.storage = memoryStorage()
}
this.limits = options.limits
this.preservePath = options.preservePath
this.fileFilter = options.fileFilter || allowAll
}
See multer's storage selection code and MemoryStorage.
With enough finagling you could multiplex storages, perhaps by writing your own storage multiplexer composite:
class StorageMultiplexer {
constructor(...storageBackends) {
this._storages = storageBackends;
}
_handleFile(req, file, cb) {
this._storages.forEach(s => s._handleFile(req, file, cb));
}
_removeFile(req, file, cb) {
this._storages.forEach(s => s._removeFile(req, file, cb));
}
}
var diskStorage = require('./storage/disk')
var memoryStorage = require('./storage/memory')
const upload = multer({
dest: 'uploads/',
storage: new StorageMultiplexer(diskStorage, memoryStorage)
});
This code is not tested and is merely a demonstration
Please read StorageEngine.md for more direction.
Related
I am trying to write an app using NodeJS and Express which will upload both a user avatar which is a single image (jpeg, png or webp) and 'legalDocs' which is essentially also a single file that can either be an image (jpeg, png or webp) or a pdf file. The avatar needs to be uploaded to ./resources/(userID)/userAvatar and the legal docs to ./resources/(userID)/legalDocs. Along with some other text info that will be posted along with the request as a multipart/form-data.
The userID is only generated after processing the text data posted.
Here's my current non-working code
APP.JS
//ENV
require("dotenv").config();
require("express-async-errors");
//EXPRESS
const express = require("express");
const server = express();
global.__basedir = __dirname;
//routes
const authRouter = require("./routes/authRoutes.js");
server.use(express.json());
server.use(express.urlencoded({extended: true}));
server.use(express.static("./public"));
server.use("/api/v0/auth", authRouter);
const port = process.env.PORT || 3000;
const start = async () => {
try {
server.listen(port, () => {
console.log(`--> Listening on port ${port}...`);
});
} catch (error) {
console.log(error);
console.log("Fatal error!");
}
}
start();
authRoutes.js:
const express = require("express");
const router = express.Router();
const {signupWithEmail} = require("../controllers/auth.js");
router.route("/signup").post(signupWithEmail);
module.exports = router;
../controllers/auth.js:
const multer = require("multer");
const processReq = require("../middleware/upload.js");
const signupWithEmail = async (req, res, next) => {
//Processing to get the ID happens here.
//For simplicity we can pretend like the ID is directly
//posted in a field with the name id.
const id = req.body.id;
console.log("id:");
console.log(id);
await processReq(id)(req, res, next);
res.status(200).send("done!");
}
upload.js
const multer = require("multer");
const errors = require("../errors");
const maxSize = 2 * 1024 * 1024;
const acceptableFileMIMETypes = ["image/jpeg", "image/png", "image/webp", "application/pdf"];
const userAvatarFilter = function (req, file, cb) {
if ((file.fieldname === "userAvatar" || file.fieldname === "legalDocs")
&& acceptableFileMIMETypes.includes(file.mimetype)
&& !(file.fieldname === "userAvatar" && file.mimetype === "application/pdf")) {
if (file.fieldname === "userAvatar") {
cb(null, true);
} else {
cb(null, false);
}
} else {
cb(new errors.BadRequestError("Both files are required, and must be JPEG, PNG, WEBP. Legal documents can also be PDF."));
}
}
const userlegalDocsFilter = function (req, file, cb) {
if ((file.fieldname === "userAvatar" || file.fieldname === "legalDocs")
&& acceptableFileMIMETypes.includes(file.mimetype)
&& !(file.fieldname === "userAvatar" && file.mimetype === "application/pdf")) {
if (file.fieldname === "userAvatar") {
cb(null, false);
} else {
cb(null, true);
}
} else {
cb(new errors.BadRequestError("Both files are required, and must be JPEG, PNG, WEBP. Legal documents can also be PDF."));
}
}
const uploadFunc = (id) => {
return (req, res, next) => {
const userAvatarStorage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, __basedir + `/resources/${id}/userAvatar`);
},
filename: (req, file, cb) => {
console.log("Uploaded avatar!");
console.log(file.originalname);
console.log(file);
cb(null, file.originalname);
}
});
const legalDocsStorage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, __basedir + `/resources/${id}/legalDocs`);
},
filename: (req, file, cb) => {
console.log("Uploaded legalDocs!");
console.log(file.originalname);
console.log(file);
cb(null, file.originalname);
}
});
const uploadAvatar = multer({
storage: userAvatarStorage,
limits: {
fileSize: maxSize,
files: 2
},
fileFilter: userAvatarFilter
}).any();
const uploadLegalDocs = multer({
storage: legalDocsStorage,
limits: {
fileSize: maxSize,
files: 2
},
fileFilter: userlegalDocsFilter
}).any();
uploadAvatar(req, res, next);
uploadLegalDocs(req, res, next);
next();
}
}
module.exports = uploadFunc;
The Problem
The first problem with my code is quite obvious, I cannot parse text data from the request unless it had been processed by multer (with the current code, console.logging the id shows that it's undefined), but I need the ID to be passed to multer in order to save the files to the correct directory. It seems like I need some way to process text only and generate the ID, then process the files.
The second problem is that, even if the ID is static - meaning that in ./controllers/auth.js the signupWithEmail function became:
const multer = require("multer");
const processReq = require("../middleware/upload.js");
const signupWithEmail = async (req, res, next) => {
const id = 1337;
console.log("id:");
console.log(id);
await processReq(id)(req, res, next);
res.status(200).send("done!");
}
I this is the output I get on the console when I try to post:
id:
1337
Uploaded legalDocs!
cours-analyse-numerique-ch1-21_22.pdf
{
fieldname: 'legalDocs',
originalname: 'cours-analyse-numerique-ch1-21_22.pdf',
encoding: '7bit',
mimetype: 'application/pdf'
}
node:_http_outgoing:771
throw new ERR_HTTP_HEADERS_SENT('remove');
^
Error [ERR_HTTP_HEADERS_SENT]: Cannot remove headers after they are sent to the client
at new NodeError (node:internal/errors:400:5)
at ServerResponse.removeHeader (node:_http_outgoing:771:11)
at write (C:\Users\der_u\source\repos\ATEEK\node_modules\finalhandler\index.js:282:9)
at AsyncResource.runInAsyncScope (node:async_hooks:204:9)
at listener (C:\Users\der_u\source\repos\ATEEK\node_modules\on-finished\index.js:170:15)
at onFinish (C:\Users\der_u\source\repos\ATEEK\node_modules\on-finished\index.js:101:5)
at callback (C:\Users\der_u\source\repos\ATEEK\node_modules\ee-first\index.js:55:10)
at IncomingMessage.onevent (C:\Users\der_u\source\repos\ATEEK\node_modules\ee-first\index.js:93:5)
at IncomingMessage.emit (node:events:513:28)
at endReadableNT (node:internal/streams/readable:1359:12) {
code: 'ERR_HTTP_HEADERS_SENT'
}
and only 'legalDocs' get uploaded.
Is there any way to solve these problems without using a temporary directory to save all the files then move them once the ID had been generated? And what does the [ERR_HTTP_HEADERS_SENT] error mean?
I'm trying to create a form where it will be a field to upload multiple images, like real estate site.
In Postman, I set the body to form-data and try to post two files to http://localhost:8080/api/files/upload while server is running to check if it is working before implementing the view in React, but getting this error:
Also addition information, I'm using
app.use("/api/files", imageRoutes); // => /api/files/
RangeError [ERR_HTTP_INVALID_STATUS_CODE]: Invalid status code: LIMIT_UNEXPECTED_FILE
at new NodeError (node:internal/errors:393:5)
at ServerResponse.writeHead (node:_http_server:343:11)
at ServerResponse._implicitHeader (node:_http_server:334:8)
at write_ (node:_http_outgoing:867:9)
at ServerResponse.end (node:_http_outgoing:977:5)
at ServerResponse.send (C:\Users\User\Downloads\yedy-react-backend-master\node_modules\express\lib\response.js:232:10)
at ServerResponse.json (C:\Users\User\Downloads\yedy-react-backend-master\node_modules\express\lib\response.js:278:15)
at C:\Users\User\Downloads\yedy-react-backend-master\server.js:35:7
at Layer.handle_error (C:\Users\User\Downloads\yedy-react-backend-master\node_modules\express\lib\router\layer.js:71:5)
at trim_prefix (C:\Users\User\Downloads\yedy-react-backend-master\node_modules\express\lib\router\index.js:326:13)
images-controllers.js
const fs = require("fs")
const uploadImage = (req, res, next) => {
const files = req.files;
if (! files) {
res.status(400).send('Please choose files');
return; }
// convert images into base64 encoding
let imgArray = files.map((file) => {
let img = fs.readFileSync(file.path)
return encode_image = img.toString('base64')
})
let result = imgArray.map((src, index) => { // create object to store data in the collection
let finalImg = {
filename: files[index].originalname,
contentType: files[index].mimetype,
imageBase64: src
}
let newUpload = new UploadModel(finalImg);
return newUpload.save().then(() => {
return {msg: `${
files[index].originalname
} Uploaded Successfully...!`}
}).catch(error => {
if (error) {
if (error.name === 'MongoError' && error.code === 11000) {
return Promise.reject({error: `Duplicate ${
files[index].originalname
}. File Already exists! `});
}
return Promise.reject({
error: error.message || `Cannot Upload ${
files[index].originalname
} Something Missing!`
})
}
})
});
Promise.all(result).then(msg => { // res.json(msg);
res.redirect('/')
}).catch(err => {
res.json(err);
})
}
exports.uploadImage = uploadImage;
multer.js
const multer = require('multer');
// set storage
var storage = multer.diskStorage({
destination : function ( req , file , cb ){
cb(null, 'uploads')
},
filename : function (req, file , cb){
// image.jpg
var ext = file.originalname.substr(file.originalname.lastIndexOf('.'));
cb(null, file.fieldname + '-' + Date.now() + ext)
}
})
module.exports = store = multer({ storage : storage })
image-routes.js
const express = require("express");
const router = express.Router()
const store = require("../middleware/multer")
const imagesController = require("../controllers/images-controllers");
router.post('/upload', store.array('images', 12), imagesController.uploadImage)
module.exports = router;
It looks like you are not sending images properly from the Postman. You should specify the name of images array to be images because you specified that in the Multer middleware.
You should send it like this:
Not able to upload image in MongoDB database using NodeJS
Middleware fieldName and key name is also same still not working. no solution on internet
server.js
import cookieParser from "cookie-parser";
import multer from "multer";
export const app = express();
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({extended: true}))
let upload = multer()
app.use(upload.array())
app.use("/",routLog,router)
app.listen(process.env.PORT,()=>{
db()
console.log(`Server is Runing on http://localhost:${process.env.PORT}`)
})
blogroute.js
import { fileUploder } from "../middelware/fileUploder.js";
export const blogRouter = Router()
blogRouter.post('/:user/create-post', fileUploder.single("file"), blogsController.createPos
This middleware file fileUplad.js which is use to store in upload folder and then from that will store in MongoDB in buffer ferment
import { randomBytes } from "crypto";
import multer from "multer";
import path from "path";
const storage = multer.diskStorage({
destination: './upload',
filename: async (req, file, callback) => {
return await randomBytes(16, (err, buf) => {
if (err) {
return reject(err);
}
const filename = buf.toString("hex") + path.extname(file.originalname);
const fileInfo = {
filename: filename,
bucketName: "upload"
};
resolve(fileInfo);
})
}
})
export const fileUploder = multer({
storage: storage,
limits: { fileSize: 2000 },
fileFilter: (req, file, callback) => {
allowedFileTypes = /jpeg|jpg|png|gif/;
if (file.mimetype == 'jpg' || file.mimetype == 'jpeg' || file.mimetype == 'png', file.mimetype == 'gif') {
return cb(null, true);
} else {
cb("Invalid file type. Only JPEG, PNG and GIF file are allowed.")
}
}
})
MulterError: Unexpected field
at wrappedFileFilter (/mnt/ssd/BlogProject/backendapi/node_modules/multer/index.js:40:19)
at Multipart.<anonymous> (/mnt/ssd/BlogProject/backendapi/node_modules/multer/lib/make-middleware.js:107:7)
at Multipart.emit (node:events:527:28)
at HeaderParser.cb (/mnt/ssd/BlogProject/backendapi/node_modules/busboy/lib/types/multipart.js:358:14)
at HeaderParser.push (/mnt/ssd/BlogProject/backendapi/node_modules/busboy/lib/types/multipart.js:162:20)
at SBMH.ssCb [as _cb] (/mnt/ssd/BlogProject/backendapi/node_modules/busboy/lib/types/multipart.js:394:37)
at feed (/mnt/ssd/BlogProject/backendapi/node_modules/streamsearch/lib/sbmh.js:248:10)
at SBMH.push (/mnt/ssd/BlogProject/backendapi/node_modules/streamsearch/lib/sbmh.js:104:16)
at Multipart._write (/mnt/ssd/BlogProject/backendapi/node_modules/busboy/lib/types/multipart.js:567:19)
at writeOrBuffer (node:internal/streams/writable:389:12)```
This is simple example :
let multer = require('multer');
let multerImage = multer().single('file');
function uploadAvatar(req, res) {
let phone = data.phoneNumber;
multerImage(req, res, () => {
let file = req.file;
if (isUndefined(file))
return Json.builder(Response.HTTP_BAD_REQUEST);
let {
fileUrl
} = File.validationAndWriteFile(file.buffer, Util.getFileFormat(file.originalname));
Update.img(phone, fileUrl, result => {
if (!result)
return Json.builder(Response.HTTP_BAD_REQUEST);
Json.builder(Response.HTTP_OK);
});
});
}
i'm trying to upload two files with different file extensions with multer from two fields but when i try it with postman the result always for the file is null, what is the solution for my problem? here is my code
middleware/uploadEpub
const multer = require('multer')
exports.uploadEpub = (epubFile, coverFile) => {
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "uploads")
},
filename: function (req, file, cb) {
cb(null, Date.now() + '-' + file.originalname.replace(/\s/g, ""))
}
})
const upload = multer({
storage
}).fields([{name: "bookFile", maxCount: 1},{name: "coverFile", maxCount: 1}])
}
controller/book
exports.addBook = async (req, res) => {
try {
const { ...data } = req.body
const newBook = await book.create({
...data,
bookFile: req.file,
coverFile: req.file
})
let bookData = await book.findOne({
where: {
id: newBook.id
},
attributes:{
exclude: ['createdAt','updatedAt']
}
})
bookData = JSON.parse(JSON.stringify(bookData))
res.send({
status: "Success",
Book: {
...bookData
}
})
} catch (error) {
console.log(error)
res.status(500).send({
status: "Failed",
message: "Server Error"
})
}
}
Multer set up
const multer = require('multer')
const path = require('path')
const { nanoid } = require('nanoid')
//Set Storage Engine
const storage = multer.diskStorage({
destination: './public/uploads/',
filename: (req, file, callback) => {
const id = nanoid(6)
const newFilename = `${file.fieldname}_${id}-${new Date().toISOString().replace(/:/g, '-')}${path.extname(file.originalname)}`
callback(null, newFilename)
}
})
const upload = multer({
storage: storage,
limits: { fileSize: 5572864 }, // up to 5.5 MB
fileFilter: (req, file, callback) => {
checkFileType(file, callback)
},
})
//Check File Type
const checkFileType = (file, cb) => {
//Allowed extensions
const fileType = /jpeg|jpg|png|gif|svg|pdf|epub/
//Check extension
const extname = fileType.test(path.extname(file.originalname).toLowerCase())
//Check mimetype
const mimetype = fileType.test(file.mimetype)
if (extname && mimetype) {
return cb(null, true)
} else {
return cb('Error: wrong file type!')
}
}
module.exports = upload
/***** middlewares.js *****/
module.exports.imageUploader = (req, res, next)=>{
const files = req.files
const uploadedFiles = []
for (var i = 0; i <images.length; i++){
uploadFiles.push('https://yourserver.com/public/uploads/' + files[i].filename)
}
req.uploadFiles = uploadFiles.toString() //appending files to req
next()
return
}
/**** index.js or app.js ****/ //where your routes are defined
router.post('/books/add', upload.array('images', 10), imageUploader, book.addBook) // allowing up to 10 files to be uploaded, calling imageUploader as a middleware
/*** Controllers/book ***/
exports.addBook = async (req, res) => {
const uploadedFiles = req.uploadFiles; //catching files from imageUploader middleware
// ... rest of your code
}
Try to upload the file as form-data in postman, and put the key with the same name that you have set in multer, the postman will look like this:
Try this:
const cpUpload = upload.fields([{ name: 'avatar', maxCount: 1 }, { name: 'gallery', maxCount: 8 }])
app.post('/cool-profile', cpUpload, function (req, res, next) {
})
The function I wrote on the node JS server does not delete the image. He can't find the picture. But when you type the name of the picture, it deletes it from the folder.
I get this error:
failed to delete local image:Error: ENOENT: no such file or directory, unlink 'C:\Users\mary\Desktop\Node.js-UploadFiles-RestAPIs\uploads\undefined'
NodeJS : File Controller
const uploadFolder = __basedir + '/uploads/';
const fs = require('fs');
exports.deleteFile=(req, res)=> {
let filename = req.params.filename;
fs.unlink(uploadFolder + filename, (err) => {
if (err) {
console.log("failed to delete local image:"+err);
} else {
console.log('successfully deleted local image');
}
});
}
NodeJS : File Router
let express = require('express');
let router = express.Router();
let fileWorker = require('../controllers/file.controller.js');
router.delete('/api/file/delete', fileWorker.deleteFile);
module.exports = router;
Service.ts :
delete(file : File):Observable<HttpEvent<{}>>{
// const formdata: FormData = new FormData();
// formdata.append('file' , file);
const req = new HttpRequest('DELETE', 'http://localhost:8000/api/file/delete',file, {
responseType: 'text'
});
return this.http.request(req);
}
component.ts :
x:any[];
deleteImage(y){
// #ts-ignore
this.x=getFile();
this.imageServis.delete(y).subscribe(event => {
console.log("Here");
});
}
See the undefined in your error :
'C:\Users\mary\Desktop\Node.js-UploadFiles-RestAPIs\uploads\undefined'
Filename is not define in your function :
exports.deleteFile=(req, res)=> {
let filename = req.params.filename;
fs.unlink(uploadFolder + filename, (err) => {
You use req.params but it seems that you send the filename in the body :
const req = new HttpRequest('DELETE', 'http://localhost:8000/api/file/delete',file, {
responseType: 'text'
});
If you want to use req.params do the following :
const req = new HttpRequest('DELETE', 'http://localhost:8000/api/file/delete'+file,null, {
responseType: 'text'
});
router.delete('/api/file/delete/:filename', fileWorker.deleteFile);