Upload .txt file in Node.js - node.js

I want to upload files for multiple fields (like html_template and preview_image fields) but Node.js is not accepting it and more over it does not logs any error in the console but in the postman it responds with internal server error.
The multer function:
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "src/uploads/");
},
filename: function (req, file, cb) {
cb(
null,
file.fieldname + "-" + Date.now() + path.extname(file.originalname)
);
},
});
var upload = multer({
storage: storage,
fileFilter: function (req, file, cb) {
var filetypes = /jpeg|jpg|png|pdf|txt/;
var mimetype = filetypes.test(file.mimetype);
var extname = filetypes.test(path.extname(file.originalname).toLowerCase());
if (mimetype && extname) {
return cb(null, true);
}
cb("Please upload valid file");
},
});
The create template route:
router
.route("/create")
.post(
upload.fields([{ name: "html_template" }, { name: "preview_image" }]),
Template.createTemplate
);
If I remove the field { name: "html_template" } from the route then it works fine but it does not work with this field html_template
The templateCreator controller function:
exports.createTemplate = catchAsync(async (req, res) => {
try {
console.log(req.files);
const template = await templateService.createTemplate(req);
return res.succeed(template, "Template created successfully");
} catch (error) {
console.trace(error);
return res.failed(500, "Internal Server Error", error);
}
});
The service function:
exports.createTemplate = async (req) => {
const name = req.body.name;
const html_template = req.files.html_template;
const preview_image = req.files.preview_image;
const imagePath = preview_image.map((image) => image.path);
const template = new Template({
name,
html_template,
preview_image: imagePath.toString(),
});
await template.save();
return template;
};
I have tried upload.fields and upload.any but it just doesn't work. I am unable to figure out why.
If I send the jpg, jpeg, png file then it accepts it and saves it to the database but not the .txt file. Why is that?

Because the MIME type for plain text files is text/plain. It doesn't contain the string txt.
It'd be better to list the full MIME types that you accept instead of doing a substring match.
The extension shouldn't really matter at that point, but if you want it to matter, you have to check it separately.

You forget to define a list of MIME types to test your file mime type with it.
I edit your fileFilter function like below and it works.
fileFilter: function (req, file, cb) {
var filetypes = /jpeg|jpg|png|pdf|txt/;
var mimeTypes = /text\/plain|image\/jpeg|image\/jpg|image\/png|application\/pdf/;
var mimetype = mimeTypes.test(file.mimetype);
var extname = filetypes.test(path.extname(file.originalname).toLowerCase());
if (mimetype && extname) {
return cb(null, true);
}
cb("Please upload valid file");
}

Related

Multer is not sending error response back when the mimetype is not matched

Here I tried to build an utility function that receives sub folder path, an array of allowed mimetypes, maximum size of the file and the default error message for mimetype. But I am facing a problem when I am trying to upload a file with different mimetype like pdf or something, I can saw the error on the console but it's not sending as a response. But it's sending the response when file is larger than the allowed maximum size of the function.
const path = require('path');
const multer = require('multer');
const createError = require('http-errors');
function uploader(sub_folder, mimetype, max_size, error_msg) {
const upload_path = path.join(__dirname, `../Public/uploads/${sub_folder}`);
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, upload_path);
},
filename: (req, file, cb) => {
const extName = path.extname(file.originalname);
const file_name = file.originalname.toLowerCase().replace(extName, "").split(" ").join("-") + "-" + Date.now() + extName;
cb(null, file_name);
}
});
const upload = multer({
storage: storage,
limits: {
fileSize: max_size,
},
fileFilter: (req, file, cb) => {
if (mimetype.includes(file.mimetype)) {
cb(null, true);
} else {
cb(createError(error_msg));
}
}
});
return upload;
}
module.exports = {
uploader,
}
Here is I called the my uploader utility function and got the multer upload object.
const multer = require('multer');
const {uploader} = require('../../Uitilies/singUpload');
const avatarUploads = (req,res,next) => {
const upload = uploader(
"avater",
["image/png", "image/jpg", "image/jpeg"],
10000000,
"Only .jpg/.jpeg/.png file with 1MB size is allowed"
);
upload.any()(req,res, (err => {
if(err){
console.log(err.message);
res.status(500).json({
errors: {
avatar: {
msg: err.message,
}
}
});
}else{
next();
}
}));
}
module.exports = avatarUploads;
Here I tried to handle the multer error manually. Because I want to handle it myself.
Here I printed the err.message. The message always print on the console but when it's error of a mimetype then the response didn't send. I have also seen that when the err instanceof multer.MulterError is true then the response is sent but when it's false the response did not send.
my router is,
router.post('/', avatarUploads, adduserValidators, adduserValidatorsHandler, addUser);
Any Solution ?????

how to store files path in a user record?

I am trying to upload files to store in a user database but its not getting stored.
This is my schema.
const UserSchema = new mongoose.Schema({
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
file: {
type: String,
},
});
<--This is my route.I am using multer to handle multiform data -->
const storage = multer.diskStorage({
destination: "public/upload",
filename: function (req, file, cb) {
cb(
null,
file.fieldname + "-" + Date.now() + path.extname(file.originalname)
);
},
});
//Initialize Upload
const upload = multer({
storage: storage,
fileFilter: function (req, file, cb) {
checkFileType(file, cb);
},
}).single("file");
//Check File Type
function checkFileType(file, cb) {
// Allowed extensions
const fileTypes = /pdf/;
//Check extensions
const extname = fileTypes.test(path.extname(file.originalname).toLowerCase());
//Check mime
const mimetype = fileTypes.test(file.mimetype);
if (mimetype && extname) {
return cb(null, true);
} else {
cb("Error:Pdf only");
}
}
router.post("/user/:id/upload", async (req, res) => {
const _id = req.params.id;
try {
const user = await User.findById(_id);
if (!user) {
res.status(404).json({ msg: "No User Found" });
}
user.file = req.file;
upload(req, res, (err) => {
if (err) {
console.log(err);
} else {
console.log(req.file);
}
I am only able to store the files in the public folder.Is there a way to store files for a particular user in db so that I can know this user uploaded this file?
Multer doesn't save files directly in your database. It only stores them in your file disk system.
So instead of doing user.file = req.file you should save the destination + filename you created with multer in the database.
You're saving the filename as file.fieldname + "-" + Date.now() + path.extname(file.originalname) in "public/images" but in your database you're just saving req.file which is completely different.
First of all let me tell you that it's not the best practice to store a file itself in a db. The best practice is to store filepath to user's object so that you can track that which file uploaded by which user.
Let say you have a route like
router.post("/", log, upload.fields([{ name: 'userFile' }]), fileUpload);
and in your fileUpload function just check if files is present, and if file is present then save file's url in the db like this
function fileUpload(req,res){
const user;
if (req.files['userFile']) {
user.filePath = `public/upload/${req.files['userFile'][0].filename}`;
const userObj = new User(user);
userObj.save();
}
}
I just gave a rough implementation to just give you an idea that how can you achieve that.

how to upload a image file in node.js?

var title="this is title";
var content="this is content";
const config = { headers: {'Accept': 'application/json', 'Content-Type': 'multipart/form-data' } };
const form = new FormData()
let file =event.target.files[0]
form.append('file', file)
form.append('title', title)
form.append('content', content)`enter code here`
Axios.post("http://localhost:3001/article/get/123", form,config ).then((res)=>{
console.log(res.data)
})
in node I have used multer for upload image or anything.
Below is the code for upload which I have used as a middleware.
const util = require("util");
const path = require("path");
const multer = require("multer");
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "./Uploads") // folder path where to upload
},
filename: function (req, file, cb) {
cb(null, file.originalname + "-" + Date.now() + path.extname(file.originalname))
}
});
const maxSize = 1 * 20000 * 20000; // file size validation
const uploadFiles = multer({ storage: storage, limits: { fileSize: maxSize } }).array("myfiles", 10); // key name should be myfiles in postman while upload
const uploadFilesMiddleware = util.promisify(uploadFiles);
module.exports = uploadFilesMiddleware;
Below is the function which I have created in controller for upload and file check.
fileUpload = async (req, res) => {
try {
let userCode = req.headers.user_code;
await upload(req, res);
if (req.files.length <= 0) {
return res.status(httpStatusCode.OK).send(responseGenerators({}, httpStatusCode.OK, 'Kindly select a file to upload..!!', true));
}
let response = [];
for (const element of req.files) {
let data = await service.addFileData(element, userCode);
response.push(data); // for file path to be stored in database
}
if (response && response.length > 0) {
return res.status(httpStatusCode.OK).send(responseGenerators(response, httpStatusCode.OK, 'File uploaded sucessfully..!!', false));
} else {
return res.status(httpStatusCode.OK).send(responseGenerators({}, httpStatusCode.OK, 'Failed to upload file kindly try later..!!', true));
}
} catch (error) {
logger.warn(`Error while fetch post data. Error: %j %s`, error, error)
return res.status(httpStatusCode.INTERNAL_SERVER_ERROR).send(responseGenerators({}, httpStatusCode.INTERNAL_SERVER_ERROR, 'Error while uploading file data', true))
}
}
and the route will go like this.
router.post('/upload/file', fileUploadController.fileUpload);
And be sure to keep same name in postman while file upload as in middleware.
The above code is in react.js. I want to do same work in node.js and the file will be upload from the public folder. main issue is how to upload image file in format like we have in frontend event.target.files[0]

Upload screenshots to google cloud storage bucket with Fluent-ffmpeg

I am currently using multer to upload videos to my google storage bucket, and fluent-ffmpeg to capture thumbnails of the videos. Videos are being uploaded into the buckets correctly, but not the thumbnails from ffmpeg. How can I change the location of the thumbnails to my google storage bucket?
Back-End Video upload
require ('dotenv').config()
const express = require('express');
const router = express.Router();
const multer = require("multer");
var ffmpeg = require('fluent-ffmpeg');
const multerGoogleStorage = require('multer-google-storage');
const { Video } = require("../models/Video");
const {User} = require("../models/User")
const { auth } = require("../middleware/auth");
var storage = multer({
destination: function (req, file, cb) {
cb(null, 'videos/')
},
filename: function (req, file, cb) {
cb(null, `${Date.now()}_${file.originalname}`)
},
fileFilter: (req, file, cb) => {
const ext = path.extname(file.originalname)
if (ext !== '.mp4' || ext !== '.mov' || ext !== '.m3u' || ext !== '.flv' || ext !== '.avi' || ext !== '.mkv') {
return cb(res.status(400).end('Error only videos can be uploaded'), false);
}
cb(null,true)
}
})
// Set location to google storage bucket
var upload = multer({ storage: multerGoogleStorage.storageEngine() }).single("file")
router.post("/uploadfiles", (req, res) => {
upload(req, res, err => {
if (err) {
return res.json({sucess: false, err})
}
return res.json({ success: true, filePath: res.req.file.path, fileName: res.req.file.filename})
})
});
Back-end thumbnail upload
router.post("/thumbnail", (req, res) => {
let thumbsFilePath = "";
let fileDuration = "";
ffmpeg.ffprobe(req.body.filePath, function (err, metadata) {
console.dir(metadata);
console.log(metadata.format.duration);
fileDuration = metadata.format.duration;
})
ffmpeg(req.body.filePath)
.on('filenames', function (filenames) {
console.log('Will generate ' + filenames.join(', '))
thumbsFilePath = "thumbnails/" + filenames[0];
})
.on('end', function () {
console.log('Screenshots taken');
return res.json({ success: true, thumbsFilePath: thumbsFilePath, fileDuration: fileDuration })
})
//Can this be uploaded to google storage?
.screenshots({
// Will take 3 screenshots
count: 3,
folder: '/thumbnails/',
size: '320x240',
//Names file w/o extension
filename:'thumbnail-%b.png'
});
});
Front-end video upload
const onDrop = (files) => {
let formData = new FormData();
const config = {
header: {'content-type': 'multipart/form-data'}
}
console.log(files)
formData.append("file", files[0])
axios.post('/api/video/uploadfiles', formData, config)
.then(response => {
if (response.data.success) {
let variable = {
filePath: response.data.filePath,
fileName: response.data.fileName
}
setFilePath(response.data.filePath)
//Thumbnail
axios.post('/api/video/thumbnail', variable)
.then(response => {
if (response.data.success) {
setDuration(response.data.fileDuration)
setThumbnail(response.data.thumbsFilePath)
} else {
alert("Failed to generate a thumbnail");
}
})
} else {
alert('Failed to save video to the server')
}
})
}
Here you can find the sample code of an application web page prompting the user to supply a file to be stored in Cloud Storage. The code is configuring bucket using environment variables and creates a new blob in the bucket to upload the file data.
I hope this information helps.
You may have to just move them after they're generated with ffmpeg.
For example, I'm writing them to a temp directory outputted by ffmpeg, and then moving after to a Cloud Storage bucket in my cloud function:
const uploadResult = await bucket.upload(targetTempFilePath, {
destination: targetStorageFilePath,
gzip: true
});
Not sure which environment you're using (flex, cloud run, etc) but these were the instructions I was referencing, and are generally the same steps you'll want to follow: https://firebase.google.com/docs/storage/extend-with-functions

Generate destination path before file upload - multer

Trying to make a folder before uploading a file to it. However, there is a problem if I do not update the server on the node when I try to download the file again, it is added to the already created folder. If I update the server, it creates a new folder and uploads it as needed. Please see the code, how can I improve it?
const db = require('../db');
const fs = require('fs');
const path = require('path');
const uuidv1 = require('uuid/v1');
const multer = require('multer');
console.log(uuidv1())
let storage = multer.diskStorage({
destination: `./uploads/${uuidv1()}`,
filename: (req, file, cb) => {
cb(null, 'test1' + '-' + Date.now() + '.' + path.extname(file.originalname));
}
});
let upload = multer({storage});
module.exports = (router) => {
router.get('/get', (req, res) => {
db.connect.query('SELECT * FROM habalka',
{type: db.sequelize.QueryTypes.SELECT}
).then(result => {
res.json(result);
})
});
router.post('/post', upload.any(), (req, res) => {
res.json('test');
});
return router;
};
Your issue is that when You start Your app it generates new uuid (once - at app startup) and passes as string to diskStorage method.
But You want to generate that path every-time when You upload a file.
So here is the solution:
Multer has feature to dynamically generate both destination path and filename.
So You've to pass a function that will generate path and return it in callback.
Example after reading this manual:
let storage = multer.diskStorage({
// pass function that will generate destination path
destination: (req, file, cb) => {
// initial upload path
let destination = path.join(__dirname, 'uploads'); // ./uploads/
// if user logged in and You store user object in session
if (req.session && req.session.user && req.session.user.id) {
destination = path.join(destination, 'users', req.session.user.id, uuidv1()); // ./uploads/users/8/generated-uuid-here/
}
else {
destination = path.join(destination, 'files', uuidv1()); // ./uploads/files/generated-uuid-here/
}
cb(
null,
destination
);
},
// pass function that may generate unique filename if needed
filename: (req, file, cb) => {
cb(
null,
Date.now() + '.' + path.extname(file.originalname)
);
}
});
My final code is here and it works !Thanks
const db = require('../db');
const fs = require('fs');
const uuid = require('uuid');
const path = require('path');
const multer = require('multer');
const shell = require('shelljs');
console.log(uuid())
let storage = multer.diskStorage({
// pass function that will generate destination path
destination: (req, file, cb) => {
// initial upload path
let destination = path.join(__dirname, '../uploads'); // ./uploads/
let id = uuid();
shell.mkdir('-p', './uploads/' + id);
destination = path.join(destination, '', id); // ./uploads/files/generated-uuid-here/
console.log('dest', destination)
cb(
null,
destination
);
},
// pass function that may generate unique filename if needed
filename: (req, file, cb) => {
let ext = file.originalname.substring(file.originalname.lastIndexOf('.'), file.originalname.length);
callback(null,file.originalname.split('.').slice(0,-1).join('.') + '-'+Date.now() +ext);
}
});
var upload = multer({storage: storage})
module.exports = (router) => {
router.get('/get', (req, res) => {
db.connect.query('SELECT * FROM habalka',
{type: db.sequelize.QueryTypes.SELECT}
).then(result => {
res.json(result);
})
});
app.post('/uploads', function (req, res) {
upload(req, res, function (err) {
if (err) {
console.log(err);
return res.end("Something went wrong!");
}else{
let ext = req.file.originalname.substring(req.file.originalname.lastIndexOf('.'), req.file.originalname.length);
var files='./uploads/'+req.file.originalname.split('.').slice(0 ,-1).join('.')+'-'+Date.now()+ext
console.log(req.file.originalname);
collection.insertOne({files},function(err,result){
if(err){
console.log("Something is Wrong");
}else{
res.json(result._id);
console.log(result);
}
})
}
});
})
return router;
};
In your multer middleware, you can do something like:
import util from "util";
import multer from "multer";
export const maxSize = 20 * 1024 * 1024;
export const __upoads_folder='/volume1/server/dash_rental_server/uploads';
let uploadFile = multer({
storage: multer.diskStorage({
destination: (req, file, cb) => {
const fileName = req.params.name;
let directoryPath = __upoads_folder + "/";
if (req?.params?.folder) { directoryPath += req.params.folder + '/' };
if (req?.params?.category) { directoryPath += req?.params?.category + '/' };
cb(null, directoryPath);
},
filename: (req, file, cb) => {
console.log(file.originalname);
cb(null, file.originalname);
},
}),
limits: { fileSize: maxSize },
}).single("file");
export let uploadFileMiddleware = util.promisify(uploadFile);
Then, in your route:
router.post("/upload/:folder/:category", async (req, res) => {
try {
await uploadFileMiddleware(req, res);
if (req.file == undefined) {
return res.status(400).send({ message: "Please upload a file!" });
}
res.status(200).send({
message: "Uploaded the file successfully: " + req.file.originalname,
});
} catch (err) {
if (err.code == "LIMIT_FILE_SIZE") {
return res.status(500).send({
message: `File size cannot be larger than ${maxSize / 1024 / 1024} MB!`,
});
}
res.status(500).send({
message: `Could not upload the file: ${req.file.originalname}. ${err}`,
});
}
});

Resources