Angular6 Rename the File to be Uploaded - node.js

I am using Angular6 and NodeJS for my API. MY application is a School Software where I should save Students Images with their Registration Numbers. I am using ng2-file-upload in Angular for File Uploads and Multer in NodeJS, Upload is working perfectly, but I can't understand how to rename the file with the registration number of Student. My url in Angular consists of Student Registration number, I just need to know how to send that Reg. Number to NodeJS and rename the file with Reg. Number
My html File
<input type="file" name="photo" ng2FileSelect [uploader]="uploader" />
<button type="button" class="btn btn-success btn-s"
(click)="uploader.uploadAll()"
[disabled]="!uploader.getNotUploadedItems().length" >
Upload an Image
</button>
My .ts file
public uploader: FileUploader = new FileUploader({url: URL, itemAlias: 'file'});
ngOnInit() {
this.uploader.onAfterAddingFile = (file) => { file.withCredentials = false; };
this.uploader.onCompleteItem = (item: any, response: any, status: any, headers: any) => {
console.log('ImageUpload:uploaded:', item, status, response);
alert('File uploaded successfully');
};
}
My NodeJS Multer Upload:
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'E:/school')
},
filename: function (req, file, cb) {
cb(null, Date.now() + '-' + file.originalname);
}
});
var upload = multer({ storage: storage });
router.post('/upload', upload.single('file'), (req, res) => {
if (!req.file) {
console.log("No file received");
return res.send({
success: false
});
} else {
console.log('file received');
return res.send({
success: true
})
}
});
Thanks in Advance. Any help will be Appreciated.

You can edit the name before posting the formData to the backend. Give the file name (or get from a form) and get the file extension from the file to upload. Then append to formData before posting to the backend.
Reference: https://stackoverflow.com/a/55178342 and https://youtu.be/v67NunIp5w8
let fileToUpload = <File>files[0];
let fileName:string = 'test-name' //get name from form for example
let fileExtension:string = fileToUpload.name.split('?')[0].split('.').pop();
const formData = new FormData();
formData.append('file', fileToUpload, fileName+'.'+fileExtension);
this.http.post(environment.backendApiUrl+'/api/upload', formData, {reportProgress: true, observe: 'events'})
.subscribe(event => {
if(event.type === HttpEventType.UploadProgress) {
this.progress = Math.round(100 * event.loaded / event.total);
} else if (event.type === HttpEventType.Response) {
this.message = 'Upload success.';
this.onUploadFinished.emit(event.body);
}
});

Be careful: This solution should work however this code is ugly and probably not the best way to do what you want. But it's just to show you how you can rename your file in the front-end side.
Important: I will assume you are uploading a single file because we will use the first item of the uploader.queue for this example.
First, you can add an input type text in your HTML file just below your input file :
<input *ngIf="uploader.queue.length" type="text" name="file" [value]="uploader.queue[0].file.name"
(change)="renameFile($event.target.value)"/>
This input will be showed when you will upload a file. The (change) will trigger the renameFile func in your component with the current input value.
Then update your TS component by adding the renameFile method :
renameFile(name: string): void {
// fetching the item uploaded
const oldFileItem: FileItem = this.uploader.queue[0];
// re-instanciating a new file based on the old file by changing the name property
const newFile: File = new File([this.uploader.queue[0]._file], name, {type: oldFileItem.file.type});
// uploader.queue is taking FileItem objects, instanciating a new FileItem
const newFileItem = new FileItem(this.uploader, newFile, this.opts);
// replacing the old one by the new file updated
this.uploader.queue[0] = newFileItem;
}
Finally, you can have a look to the file property in your console.log inside the onCompleteItem function, the file has been updated.

You should be able to watch the onFileSelected event and get file[0] (if you are single file upload).
And set the file[0].name before uploading.

Related

validate request body before uploading a file using multer in nodejs

I want to upload user profiles according to their school ids
// import multer
var multer = require('multer')
// import school and student models
const { School } = require('./models/school/school.model.js')
const { Student } = require('./models/student/student.model.js')
// configure multer
const storage = multer.diskStorage({
destination: (req, file, cb) => {
const dir = `./uploads/schools/${req.body.schoolId}/students`
fs.exists(dir, exist => {
if (!exist) {
return fs.mkdir(dir, { recursive: true }, error => cb(error, dir))
}
return cb(null, dir)
})
},
filename: (req, file, cb) => {
cb(null, `student-${normaliseDate(new Date().toISOString())}.${file.originalname.split('.')[file.originalname.split('.').length - 1]}`)
}
})
var upload = multer({storage: storage}).single('profile')
app.post('/student', function (req, res) {
// check if submitted school id is valid
let school = await School.findOne({ _id: req.body.schoolId })
if (!school)
return res.send(`School of id ${req.body.schoolId} doesn't exist`)
// upload the profile pic
upload(req, res, function (err) {
if (err)
return res.send(err)
})
// save the student
let newStudent = new Student({
name: req.body.name,
email: req.body.email,
school: req.body.schoolId,
profile: req.file.filename
})
const saveDocument = await newStudent.save()
if (saveDocument)
return res.send(saveDocument).status(201)
return res.send('New College not Registered').status(500)
})
but when I try to access req.body before uploading the profile I get an empty object.
I would upload the profiles in a tempolary folder and move them later but what if the submitted school id is invalid ?? this will require me to delete the uploaded file. so I want to make sure the submitted details are valid and then upload the profile later save the student.
Check your codes, you are using await but async is nowhere to be found. Though it may not be the cause of getting an empty req.body object.
let school = await School.findOne({ _id: req.body.schoolId })
const saveDocument = await newStudent.save()
I am not sure of how you are sending data to the backend but try these option it may work
remember that when javascript is sending POST data from client to server it sends them according to how they are arranged so if you have your client form like this
<form action="">
<input type="file" name="pic">
<input type="text" name="username">
<input type="number" name="age">
</form>
it will send file named pic first, then text named username secondly and finally send number named age, so if you are getting empty req.body while the file has not been completely uploaded you might check this if it is the problem because javascript process them on an order according to how they were sent
i hope this gonna help

NODE GridFStorage with additional data from request doesn't always show up

Hey guys i'm confronting a very strange behaviour with GridFs while i try to upload a file:
So i send with formdata my file which i want to upload and a code which will be set in metadata in files,
the files are saved correctly and the originalname field is always added to the metadata but the code field which is a req.body paramater has a very strange behaviour.
files.ts
uploadFileFormSubmit(event) {
const formData = new FormData();
formData.append('file', event.target.files.item(0));
formData.append('code', this.courseCode);
this.fileService.uploadFile(formData).subscribe(res => ....
fileService.ts
uploadFile(data): Observable<GeneralResponse> {
return this.http.post<GeneralResponse>('/files/uploadFile', data);
}
here is the backend part:
files.js (back-end)
const storage = new GridFsStorage({
url: dbUrl,
file: (req, file) => {
return new Promise((resolve, reject) => {
crypto.randomBytes(16, (err, buf) => {
if (err) {
return reject(err);
}
const filename = buf.toString('hex') + path.extname(file.originalname);
console.log(req.body)
const code = JSON.parse(JSON.stringify(req.body));
console.log(code)
const fileInfo = {
filename: filename,
metadata: {
originalname: file.originalname,
materialCode: code.code
},
bucketName: 'files'
};
resolve(fileInfo);
});
});
}
});
As you can see i parse the req.body in order to get my property (i found it here this solution because the req.body was [Object: null prototype] { code: 'myCode'} )
And for some files this code data is passed but not always.
note that there are 2 console.logs(before and after JSON.parse()
the first object null is an excel file,
the second is a pdf
the third is jpg file
the fourth is a png file
maybe it's something with the extensions but i cannot imagine why the req.body sometimes gets parsed
and the code gets into metadata but other times not :/
So what can cause this behaviour? thanks for help in advance :D

multer and node/express file upload not working with Angular

I'm trying to do single file upload using multer. Although I'm able to upload a file manually in the relevant folder using tools like postman for testing the express route, I cannot upload it using Angular frontend. I created a folder called uploads in the node backend folder where the files are supposed to get uploaded. Also I need to upload the file within a form and pass it to a api where it should take the file along with other parameters also. But unfortunately, it is returning status 500 with Internal Server Error on the browser console while on my node terminal it is returning Cannot read property 'path' of undefined.
My node backend code which is working fine is below:
const multer = require('multer')
const storage = multer.diskStorage({
destination: function(req, file, cb) {
cb(null, './uploads/')
},
filename: function(req, file, cb) {
cb(null, Date.now() + file.originalname)
}
})
const upload = multer({storage: storage})
let baseUrl = appConfig.apiVersion+'/blogs';
app.post(baseUrl+'/create', upload.single('imagePath'), (req, res) => {
var today = time.now()
let blogId = shortid.generate()
let newBlog = new BlogModel({
blogId: blogId,
title: req.body.title,
description: req.body.description,
bodyHtml: req.body.blogBody,
isPublished: true,
category: req.body.category,
author: req.body.fullName,
created: today,
lastModified: today,
imagePath: req.file.path //node console is pointing towards this point
}) // end new blog model
let tags = (req.body.tags != undefined && req.body.tags != null && req.body.tags != '') ? req.body.tags.split(',') : []
newBlog.tags = tags
newBlog.save((err, result) => {
if (err) {
console.log(err)
res.send(err)
} else {
res.send(result)
}
}) // end new blog save
});
Below is my Angular Component code which is not working:
selectImage(event) {
if(event.target.files.length > 0){
const file = event.target.files[0];
this.images = file;
}
}
public createBlog(): any {
const formData = new FormData();
const form = formData.append('imagePath', this.images);
let blogData = {
title: this.blogTitle,
description: this.blogDescription,
blogBody: this.blogBodyHtml,
category: this.blogCategory,
imagePath: form
} //end blogData
console.log(blogData);
this.blogHttpService.createBlog(blogData).subscribe(
data => {
console.log(data);
this.toastr.successToastr('Blog Posted Susseccfully!', 'Success!');
setTimeout(() =>{
this.router.navigate(['blog', data.blogId]);
}, 1000)
},
error => {
console.log(error);
console.log(error.errorMessage);
this.toastr.errorToastr('This is not good!', 'Oops!');
})
}
Angular Service code
public createBlog(blogData): any {
let myResponse = this._http.post(this.baseUrl + '/create', blogData);
return myResponse;
}
Frontend HTML Code:
<div>
<input type="file" name="imagePath" (change)="selectImage($event)" />
</div>
It seems like you created a formData object, but you are not actually doing anything with it. As you can see here, you are building up an object and sending it along with your request, but it does not include your formData
let blogData = {
title: this.blogTitle,
description: this.blogDescription,
blogBody: this.blogBodyHtml,
category: this.blogCategory,
imagePath: this.imagePath
} //end blogData
console.log(blogData);
this.blogHttpService.createBlog(blogData).subscribe(
Not entirely sure what the exact syntax would be in your case, but here you can see some sample code I have in a project of mine which will hopefully give you an idea.
changeHandler(e) {
const fd = new FormData();
fd.append('sample', e.target.files[0]);
axios.post('/save-image', fd).then(i => {
this.setState({img: i.data.filename});
});
}
As you can see, the formData is what I am actually sending to the server.

Not able to upload mutiple files to local directory using multer file in node and angular2

I have a requirement where I want to upload multiple images in node.js. so I am using angular2 for frontend. I am using formData to send the files to node part.
below is the code :
public setAnswerImage(obj:any,files:any):Promise<any> {
try {
let urlDetails = obj["id"] ? ANSWER_URLS['updateimages'] : ANSWER_URLS['insertimages'];
let formData = new FormData();
Object.keys(obj).forEach(key =>{
formData.append(key,obj[key]);
});
console.log(formData)
for(let i =0; i < files.length; i++){
formData.append("uploads[]", files[i],files[i]['name']);
}
console.log(formData)
return this.restUtils.post(urlDetails, formData, {"id":obj["id"]});
}
catch(err) {
return Promise.reject(err);
}
}
let urlDetails = obj["id"] ? ANSWER_URLS['updateimages'] : ANSWER_URLS['insertimages'];
If obj contains id I will be calling update method else insert method.
export const ANSWER_URLS = {
insertimages : { url : "/api/answer/response/insertimages"}
}
In Node part I am using multer file storage, i have created a folder as temp and path has been set to this folder in multer as shown below.
router.post("/response/insertimages", upload.array("uploads[]", 12), (req: Request, res: Response, next: NextFunction) => {
new AnswerRoute().saveAnswerImages(req, res, next);
});
var storage = multer.diskStorage({
destination: (req: Request, file, cb) => {
cb(null, video_path.dest + 'temp//');
}, filename: (req:Request, file, cb) => {
let name = file.originalname.split('.');
console.log(name,"mnae")
cb(null, new Date().getTime().toString() + '.' + name[name.length-1]);
}
})
var upload = multer({ storage: storage });
Console.log output shows multiple files I uploaded but in temp folder, I can see only one :
[ 'doctor', 'jpg' ] 'mnae'
[ 'Doctorvisits', 'png' ] 'mnae'
The above output shows two images I uploaded but only one file is saved in the temp folder.
I am using upload.array("uploads[]",12), where uploads contain multiple files but the problem is when I upload multiple files only first file gets uploaded to the temp folder.
Am I going wrong somewhere?
Please help me

What is the best way to multiple file upload through a single input in nodejs express 4?

I'm using nodejs with express 4. I'm trying to upload multiple file through a single input field. Also I submit my form through ajax.
I'm using express-fileupload middleware for uploading files. When i upload multiple files, it works fine. But when upload a single file, it's not working.
html code-
<input type="file" name="attach_file" id="msg_file" multiple />
ajax code-
var data = new FormData();
$.each($('#msg_file')[0].files, function(i, file) {
data.append('attach_file', file);
});
$.ajax({
url: '/send_message',
data: data,
cache: false,
contentType: false,
processData: false,
method: 'POST',
type: 'POST',
success: function(data){
console.log(data);
}
});
server side js code-
router.post('/send_message', function(req, res, next){
if (!req.files)
res.json('No files were uploaded.');
let sampleFile = req.files.attach_file;
console.log(sampleFile.length);
var file_info = [];
var count = 0;
sampleFile.forEach(function(ele, key) {
ele.mv(path.resolve(`./public/upload/${ele.name}`), function(err) {
if (err){
console.log(err);
}else{
file_info.push(ele.name);
}
count++;
if(sampleFile.length == count){
res.json({file_name: file_info });
}
});
});
});
if i upload a single file console.log(sampleFile.length); show undefined.
After different kind of testing, I found the issue. Everything is ok except when I upload a single file.
When ajax send a single file, it was not an array. That's why, length was undefined and forEach did not run. Need to check first, then use mv() function, like as-
if(sampleFile instanceof Array){
// here is the forEach block
}else{
// run a single mv() function
}
Another complete example and also late answer; in case that help someone.
this is not ajax-based; I used a form in client-side; but you can send data using JS and XHR.
It supports both single and also multiple uploads.
upload.js
'use strict';
const fss = require('fs')
const pth = require('path');
const exp = require('express');
const swg = require('swig');
const efm = require("formidable");
const app = exp();
const thm = swg.compileFile(pth.join(__dirname, '', 'upload.html'));
app.listen(9009);
app.get(`/`, async (q, r) => r.send(thm({ msg: "Select a File to Upload" })));
app.get(`/:msg`, async (q, r) => r.send(thm({ msg: q.params.msg })));
app.post('/upload', (r, q) => {
const form = efm({ multiples: true });
form.parse(r, (e, p, files) => {
let dir = pth.join(__dirname, '', '/media/');
if (!fss.existsSync(dir)) fss.mkdirSync(dir);
let uploaded = 0, exists = 0, error = 0;
//if single file uploaded
if(!Array.isArray(files.file)){
files.file=[files.file]
}
files.file.forEach(f => {
let nPth = dir + f.name;
try {
fss.accessSync(nPth, fss.F_OK);
exists++;
} catch (file_e) {
let err = fss.renameSync(f.path, nPth);
if (err) error++; else uploaded++;
}
});
q.redirect(`Upoader -> All: ${files.file.length}, Uploaded: ${uploaded}, Existed: ${exists}, Error: ${error}`);
});
});
**Its better use "A-Sync" functions.
**I think its also better if you run uploader script on another port.
upload.html
<h3>{{msg}}</h3>
<br/>
<form action="upload" method="post" enctype="multipart/form-data">
<input type="file" name="file" multiple>
<input type="submit">
</form>

Resources