Schema in Mongoose and Gridfs - javascript

I am using Mongoose to upload files to database. I have no problems uploading files and even viewing the names of files stored in the database, the only problem is that I can't download or delete the files.
I don't have a clue why I can't download my files especially when I can actually retrieve the file's metadata (ID) by Gridfs. Here's my code.
const storage = new GridFsStorage({
url: mongoURI,
cache: true, // cache
file: (req, file) => {
return file.originalname + Date.now();
}
});
const upload = multer({ storage });
server.get("/upload", (req,res) =>{
res.render('upload');
});
// #route POST /upload
server.post('/upload', upload.single('file'), (req, res) => {
res.redirect("/");
});
server.get('/:file_id', function(req , res) {
var file_id = req.params.file_id;
gfs.files.find({_id: file_id}).toArray(function (err, files) {
if (err) {
res.json(err);
}
if (files.length > 0) {
var mime = files[0].contentType;
var filename = files[0].filename;
res.set('Content-Type', mime);
res.set('Content-Disposition', "inline; filename=" + filename);
var read_stream = gfs.createReadStream({_id: file_id});
read_stream.pipe(res);
} else {
res.json(file_id+ ' This file does not exist.');
}
});
});
Below is a ejs file that gets file_id to server.
<div class="card card-body mb-3">
<%= file.filename %>
<form action= "/delete<%= file._id %>" method="post">
<button class="delete">DELETE</button>
</form>
<form action="/<%= file._id %>" method="get">
<input type="submit" value="DOWNLOAD" class="btn btn-primary btn-block">
</form>
</div>
What I can see when I try to download the files is :
:62bb571d71c40d24ea68b589 This file does not exist.
And the url of the page is:
http://localhost:3000/62bb571d71c40d24ea68b589?

Related

req.param.id brings "undefined"

Getting req.param undefined
Hi, I have a similar error to the one above.
server.get('/:file_id', function(req , res) {
var file_id = req.params.id;
console.log(file_id);
gfs.files.find({_id: file_id}).toArray(function (err, files) {
if (err) {
res.json(err);
}
if (files.length > 0) {
var mime = files[0].contentType;
var filename = files[0].filename;
res.set('Content-Type', mime);
res.set('Content-Disposition', "inline; filename=" + filename);
var read_stream = gfs.createReadStream({_id: file_id});
read_stream.pipe(res);
} else {
res.json(file_id+ ' This file does not exist.');
}
});
});
Below is a ejs file that gets file_id to server.
<div class="card card-body mb-3">
<%= file.filename %>
<form action="/:<%= file._id %>" method="get">
<input type="submit" value="DOWNLOAD" class="btn btn-primary btn-block">
</form>
</div>
What I get from this code is : "undefined This file does not exist."
I think the problem lies on req.params.id but am not sure where to fix.
Thank you.

Nodejs POST a FILE

I'm trying to upload a file from webpage to my backend but nothing happends. There is what i did:
Here's the form from the html file:
<form action="/api/bulk" method="POST" enctype="multipart/form-data">
<div style="width: 200px">
<input
type="file"
id="user_group_logo"
class="custom-file-input"
accept=".xlsx, .xls, .csv"
name="file"
/>
<label id="user_group_label" for="user_group_logo">
<i class="fas fa-upload"></i> Choose a file...
</label>
<button class="btn btn-primary" type="submit">Upload</button>
<div class="text-center"></div>
<div class="text-center mt-2"></div>
</div>
</form>
here's the routing:
router.route('/api/bulk').post(modelController.postBulk);
and here's the controller method that should upload the file to /uploads folder
var multer = require('multer');
const storage = multer.diskStorage({
destination: './uploads',
});
const upload = multer({
storage: storage,
});
exports.postBulk = async (req, res) => {
try {
console.log('test');
upload(req, res, (err) => {
if (err) {
console.log('error');
} else {
console.log(req.file);
res.send('test-req.file');
}
});
} catch (err) {
res.status(404).json({
status: 'fail',
message: err.message,
attention: 'Cannot verify the CSV file. Call support!',
});
}
};
I don't get either a message in the console, so the method is not "accessed" somehow (I should get a "test" when I try to upload the file at least).
Any ideas, please?
Try this
const express = require('express');
const router = express.Router();
const multer = require('multer');
const storage = multer.diskStorage({
destination: './uploads',
});
const upload = multer({
storage: storage,
});
const postBulk = async (req, res) => {
try {
console.log(req.file);
req.status(200).json({})
} catch (err) {
res.status(404).json({
status: 'fail',
message: err.message,
attention: 'Cannot verify the CSV file. Call support!',
});
}
};
router.post(`/api/bulk`, upload.single('file'), postBulk);
try this
router.route('/api/bulk').post(upload.single('file'),modelController.postBulk);
upload is your constant holding multer configuration
.single is for your single file upload and .array is for multiple file upload.
please check req.file inside your controller to get the file uploaded via form

how express forming the img URL

I have created a simple node solution which contains a form and on that submit the form it will display the image that is being inserted in the form.
app.js
const app = express()
app.use(express.static('public'))
app.engine('hbs',handlebars({
layoutsDir : __dirname + '/views/layouts',
defaultLayout : "mainlayout",
extname : "hbs",
partialsDir : __dirname + '/views/partials'
}))
app.use("/uploader", imgUploader)
app.set('view engine','hbs')
impUpload.js
const express = require('express')
const route = express.Router();
const multer = require('multer');
const path = require('path');
const Storage = multer.diskStorage({
destination: './public/uploads',
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now() + path.extname(file.originalname))
}
})
const upload = multer({
storage: Storage,
fileFilter: function (req, file, cb) {
checkFileType(file, cb);
}
}).single('myImage');
function checkFileType(file, cb) {
const filetypes = /jpeg|jpg|png|gif/;
const extname = filetypes.test(path.extname(file.originalname).toLowerCase())
const mimeType = filetypes.test(file.mimetype);
if (extname && mimeType) {
return cb(null, true)
}
else {
cb('Error: Images Only!!!');
}
}
route.get("/", (req, res) => {
console.log("inside imgupload folder")
res.render("fileUpload")
})
route.post("/uploaded", (req, res) => {
upload(req, res, (error) => {
if (error) {
res.render("fileUpload", { message: error })
}
else {
if (req.file == undefined) {
res.render("fileUpload", { message: 'Please upload a file' })
}
else {
res.render('fileUpload', {
message: 'File Uploaded Successfully',
file: `uploads/${req.file.filename}`
});
}
}
})
})
module.exports = route
fileUpload.js
<div class="container">
<h1>file upload</h1>
{{message}}
<form action="/uploader/uploaded" method="post" enctype="multipart/form-data">
<div class="file-field input-field">
<div class="btn">
<span>File</span>
<input name="myImage" type="file">
</div>
<div class="file-path-wrapper">
<input class="file-path validate" type="text">
</div>
</div>
<button type="submit" class="btn n">Submit</button>
</form>
<br>
</div>
<div>
{{#if file}}
<img src="{{file}}" class="responsive-img">
{{/if}}
</div>
Currently, my solution is structured as below
I am getting the error in the console
GET http://localhost:3000/uploader/uploads/myImage-1589223958713.PNG 404 (Not Found)
I am not getting why it's trying to find that img in the uploader/uploads although I have defined public folder in the app.js
But when trying the same code in the app.js it's working absolutely fine.
also if I try express().use(express.static(path.join(__dirname , '../public'))) in the imgupload.js then i am getting the error
Not allowed to load local resource: file:///C:/arunoday/node/login_express_mongo/public/uploads/myImage-1589220613014.PNG
any help would be appreciated.
This is just how browser's handle relative paths.
You have a Handlebars template that contains the following:
<img src="{{file}}" class="responsive-img">
The value of file is set to uploads/${req.file.filename}, which becomes something like uploads/myImage-1589223958713.PNG.
When your template is executed with above value for file you get:
<img src="uploads/myImage-1589223958713.PNG" class="responsive-img">
When the browser sees a relative URL, like uploads/myImage-1589223958713.PNG, it has to figure out the absolute URL. Since this relative URL does not begin with a /, the browser thinks it is a child path of the current page URL.
If the current page URL is http://localhost:3000/uploaded/uploader, the browser thinks your uploads/myImage-1589223958713.PNG URL is a child of http://localhost:3000/uploader/ and so produces: http://localhost:3000/uploader/uploads/myImage-1589223958713.PNG.
To get the correct URL, you want to set the value for file so that it includes the full path:
file: `/uploads/${req.file.filename}`
Update:
Note that /public should not be used included in the value for file because the /public directory is registered with express as a directory in which to look for static assets.

How to pass multiple values while uploading image to server(File Upload with AngularJS and NodeJS)?

Client side:
I have done file upload with AngularJS and NodeJS it's working but while uploading file i need to pass 'name' and 'email' to server.
Server side:
After uploading file into folder i need to save file path, name and email into database. How can i do this?
angular.module('fileUpload', ['ngFileUpload'])
.controller('MyCtrl',['Upload','$window',function(Upload,$window){
var vm = this;
vm.submit = function(){ //function to call on form submit
if (vm.upload_form.file.$valid && vm.file) { //check if from is valid
vm.upload(vm.file); //call upload function
}
}
vm.upload = function (file) {
console.log(vm.name);
console.log(vm.email);
Upload.upload({
url: 'http://localhost:3000/upload', //webAPI exposed to upload the file
data:{file:file} //pass file as data, should be user ng-model
}).then(function (resp) { //upload function returns a promise
if(resp.data.error_code === 0){ //validate success
$window.alert('Success ' + resp.config.data.file.name + 'uploaded. Response: ');
} else {
$window.alert('an error occured');
}
}, function (resp) { //catch error
console.log('Error status: ' + resp.status);
$window.alert('Error status: ' + resp.status);
}, function (evt) {
console.log(evt);
var progressPercentage = parseInt(100.0 * evt.loaded / evt.total);
console.log('progress: ' + progressPercentage + '% ' + evt.config.data.file.name);
vm.progress = 'progress: ' + progressPercentage + '% '; // capture upload progress
});
};
}]);
<script src="http://cdn.bootcss.com/danialfarid-angular-file-upload/12.2.13/ng-file-upload-shim.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-file-upload/2.4.1/angular-file-upload.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<html>
<head>
<title>Home</title>
</head>
<body ng-app="fileUpload">
<h1>Angular Node File Upload</h1>
<form ng-controller="MyCtrl as up" name="up.upload_form">
name
<input type="text" ng-model="up.name"><br> <br>
email
<input type="text" ng-model="up.email"><br>
Image
<input
type="file"
ngf-select
ng-model="up.file"
name="file"
ngf-max-size="20MB"
/>
Image thumbnail: <img style="width:100px;" ng-show="!!up.file" ngf-thumbnail="up.file || '/thumb.jpg'"/>
<i ng-show="up.upload_form.file.$error.required">*required</i><br>
<i ng-show="up.upload_form.file.$error.maxSize">File too large
{{up.file.size / 1000000|number:1}}MB: max 20M</i>
<!-- Multiple files
<div class="button" ngf-select ng-model="up.files" ngf-multiple="true">Select</div>
Drop files: <div ngf-drop ng-model="up.files" class="drop-box">Drop</div> --><br>
<button type="submit" ng-click="up.submit()">submit</button>
<p>{{up.progress}}</p>
</form>
</body>
</html>
Backend code:
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var multer = require('multer');
app.use(function(req, res, next) { //allow cross origin requests
res.setHeader("Access-Control-Allow-Methods", "POST, PUT, OPTIONS, DELETE, GET");
res.header("Access-Control-Allow-Origin", "http://localhost");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
/** Serving from the same express Server
No cors required */
app.use(express.static('../client'));
app.use(bodyParser.json());
var storage = multer.diskStorage({ //multers disk storage settings
destination: function (req, file, cb) {
cb(null, './uploads/');
},
filename: function (req, file, cb) {
var datetimestamp = Date.now();
cb(null, file.fieldname + '-' + datetimestamp + '.' + file.originalname.split('.')[file.originalname.split('.').length -1]);
}
});
var upload = multer({ //multer settings
storage: storage
}).single('file');
/** API path that will upload the files */
app.post('/upload', function(req, res) {
console.log(req.body);
upload(req,res,function(err){
if(err){
res.json({error_code:1,err_desc:err});
return;
}
res.json({error_code:0,err_desc:null});
});
});
app.listen('3000', function(){
console.log('running on 3000...');
});
i tried like this
Upload.upload({
url: 'http://localhost:3000/upload', //webAPI exposed to upload the file
data:{file:file, name:vm.name, email:vm.email} //pass file as data, should be user ng-model
})
backend
app.post('/upload', function(req, res) {
console.log(req.body);
console.log(req.file);
upload(req,res,function(err){
if(err){
res.json({error_code:1,err_desc:err});
return;
}
res.json({error_code:0,err_desc:null});
});
});
in front end(angular)i am getting value what ever i entered in form but backend(nodejs) i am getting undefined value
You need to amend your angular code to send the extra info in the data of the request
Upload.upload({
url: 'http://localhost:3000/upload', //webAPI exposed to upload the file
data:{file:file, name:vm.name, email:vm.email} //pass file as data, should be user ng-model
})
Then in your backend code you can reference this on the body of the request
req.body.name
req.body.email
I not sure if my answer will help you. But you can add in your data the name and the email.
Upload.upload({
url: 'http://localhost:3000/upload', //webAPI exposed to upload the file
data:{file:file,name:"putHeretheName",email:"putHereTheMail"} //pass file as data, should be user ng-model
}
Then server side you can create a function or complete your actual "/upload" with a query that save in your bdd what you want. You just have to get the name of the path created and then do the save in case of your upload is successful.
Maybe this e.g will help you more: https://www.npmjs.com/package/ng-file-upload
You can add a file property that will hold the file and update the data property to contain email and name inside the Upload.upload object. This way you can get them easily at the server side.
Note: I have updated my answer, I wrapped all the values Angular viewis emitting inside a params object. I also changed angular-ng-upload CDN wasn't working on codepen. You should also load Angular.js first.
view
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://angular-file-upload.appspot.com/js/ng-file-upload-shim.js"></script>
<script src="https://angular-file-upload.appspot.com/js/ng-file-upload.js"></script>
<html>
<head>
<title>Home</title>
</head>
<body ng-app="fileUpload">
<h1>Angular Node File Upload</h1>
<form ng-controller="MyCtrl as up" name="up.upload_form">
name
<input type="text" ng-model="up.params.name"><br> <br>
email
<input type="text" ng-model="up.params.email"><br>
Image
<input
type="file"
ngf-select
ng-model="up.params.file"
name="file"
ngf-max-size="20MB"
/>
Image thumbnail: <img style="width:100px;" ng-show="!!up.params.file" ngf-thumbnail="up.params.file || '/thumb.jpg'"/>
<i ng-show="up.upload_form.params.file.$error.required">*required</i><br>
<i ng-show="up.upload_form.params.file.$error.maxSize">File too large
{{up.params.file.size / 1000000|number:1}}MB: max 20M</i>
<!-- Multiple files
<div class="button" ngf-select ng-model="up.files" ngf-multiple="true">Select</div>
Drop files: <div ngf-drop ng-model="up.files" class="drop-box">Drop</div> --><br>
<button type="submit" ng-click="up.submit()">submit</button>
<p>{{up.progress}}</p>
<p>{{up.params}}{{up.params.file.size}}</p>
</form>
</body>
</html>
Angular
var vm = this;
vm.submit = function(){
if (vm.upload_form.file.$valid && vm.params.file) {
console.log(vm.params)
vm.upload(vm.params); // Pass the `vm.params` object.
}
}
vm.upload = function (params) {
console.log(params.name); // params.name should be available
console.log(params.email); // params.email should be available
console.log(params.file); // params.file should be available
Upload.upload({
url: 'http://localhost:3000/upload',
file: params.file, // Image to upload
data: {
name: params.name,
email: params.email
}
})
}
Node.js/express
app.post('/upload', function(req, res) {
console.log(JSON.parse(req.body.data)); // email and name here
console.log(req.files.file); // file object
upload(req,res,function(err){
if(err){
res.json({error_code:1,err_desc:err});
return;
}
res.json({error_code:0,err_desc:null});
});
});

Uploading multiple files with Multer

I'm trying to upload multiple images using Multer. It all works as expected except that only one file is being uploaded (the last file selected).
HTML
<form class='new-project' action='/projects' method='POST' enctype="multipart/form-data">
<label for='file'>Select your image:</label>
<input type='file' multiple='multiple' accept='image/*' name='uploadedImages' id='file' />
<span class='hint'>Supported files: jpg, jpeg, png.</span>
<button type='submit'>upload</button>
</form>
JS
//Define where project photos will be stored
var storage = multer.diskStorage({
destination: function (request, file, callback) {
callback(null, './public/uploads');
},
filename: function (request, file, callback) {
console.log(file);
callback(null, file.originalname)
}
});
// Function to upload project images
var upload = multer({storage: storage}).any('uploadedImages');
// add new photos to the DB
app.post('/projects', function(req, res){
upload(req, res, function(err){
if(err){
console.log(err);
return;
}
console.log(req.files);
res.end('Your files uploaded.');
console.log('Yep yep!');
});
});
I get the feeling I'm missing something obvious...
EDIT
Code I tried following Syed's help:
HTML
<label for='file'>Select your image:</label>
<input type='file' accept='image/*' name='uploadedImages' multiple/>
<span class='hint'>Supported files: jpg, jpeg, png.</span>
<input type="submit" value="uploading_img">
JS
multer = require('multer'),
var upload = multer();
app.post('/projects', upload.array('uploadedImages', 10), function(req, res, err) {
if (err) {
console.log('error');
console.log(err);
}
var file = req.files;
res.end();
console.log(req.files);
});
Uploading multiple files with Multer
NodeJs Code
Set require files and Storage
const express = require('express');
const multer = require('multer');
const path = require('path');
const app = express();
const port = 3000
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, path.join(__dirname, './images/'))
},
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now() + file.originalname.match(/\..*$/)[0])
}
});
Set upload file limit or validataion
const multi_upload = multer({
storage,
limits: { fileSize: 1 * 1024 * 1024 }, // 1MB
fileFilter: (req, file, cb) => {
if (file.mimetype == "image/png" || file.mimetype == "image/jpg" || file.mimetype == "image/jpeg") {
cb(null, true);
} else {
cb(null, false);
const err = new Error('Only .png, .jpg and .jpeg format allowed!')
err.name = 'ExtensionError'
return cb(err);
}
},
}).array('uploadedImages', 2)
Create the main route for uploading
app.post('/projects', (req, res) => {
multi_upload(req, res, function (err) {
if (err instanceof multer.MulterError) {
// A Multer error occurred when uploading.
res.status(500).send({ error: { message: `Multer uploading error: ${err.message}` } }).end();
return;
} else if (err) {
// An unknown error occurred when uploading.
if (err.name == 'ExtensionError') {
res.status(413).send({ error: { message: err.message } }).end();
} else {
res.status(500).send({ error: { message: `unknown uploading error: ${err.message}` } }).end();
}
return;
}
// Everything went fine.
// show file `req.files`
// show body `req.body`
res.status(200).end('Your files uploaded.');
})
});
Listen port
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
});
HTML code
<form id="form_el" class='new-project' action='/projects' method='POST' enctype="multipart/form-data">
<label for='file'>Select your image:</label>
<input type='file' multiple='multiple' accept='image/*' name='uploadedImages' id='file' />
<span class='hint'>Supported files: jpg, jpeg, png.</span>
<button type='submit'>upload</button>
</form>
JAVASCRIPT CODE
form_el.addEventListener('submit', async function (e) {
const files = e.target.uploadedImages.files;
if (files.length != 0) {
for (const single_file of files) {
data.append('uploadedImages', single_file)
}
}
});
const submit_data_fetch = await fetch('/projects', {
method: 'POST',
body: data
});
Here you go for this example:
var multer = require('multer');
var upload = multer();
router.post('/projects', upload.array('uploadedImages', 10), function(req, res) {
var file = req.files;
res.end();
});
<form action="/projects" method="post" enctype="multipart/form-data">
<input type="file" name="uploadedImages" value="uploading_img" multiple>
<input type="submit" value="uploading_img">
</form>
Visit for more info about Multer.
My guess is that for each file that you want to upload, you reclick:
<input type='file' multiple='multiple' accept='image/*' name='uploadedImages' id='file' />
If you do this, then only the last file selected will be uploaded, as you overwrite the previous selected files.
To upload multiple files, you have to select them all at once in the file picker.

Categories

Resources