I've a csv file and want to dump the data in it into mongodb using angular and nodejs.
Need help to read csv file's data using angular and parse it and store in mongodb
import { Injectable } from '#angular/core';
import { Http } from '#angular/http';
import 'rxjs/add/operator/map';
#Injectable()
export class TemplateService {
domain = 'http://localhost:3000';
constructor(private http: Http) { }
postCsvToMongo(csvJson: any) {
console.log('CSV!!!' + csvJson);
return this.http.post(this.domain + '/fu/fileupload', csvJson).map(res => res.json());
}
}
this is the code i'm using to send json object over post routes.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>TypeError: Converting circular structure to JSON
<br> at JSON.stringify (<anonymous>)
<br> at router.post (/home/agam/projects/AMEX/routes/fileUpload.js:10:23)
<br> at Layer.handle [as handle_request] (/home/agam/projects/AMEX/node_modules/express/lib/router/layer.js:95:5)
<br> at next (/home/agam/projects/AMEX/node_modules/express/lib/router/route.js:137:13)
<br> at Route.dispatch (/home/agam/projects/AMEX/node_modules/express/lib/router/route.js:112:3)
<br> at Layer.handle [as handle_request] (/home/agam/projects/AMEX/node_modules/express/lib/router/layer.js:95:5)
<br> at /home/agam/projects/AMEX/node_modules/express/lib/router/index.js:281:22
<br> at Function.process_params (/home/agam/projects/AMEX/node_modules/express/lib/router/index.js:335:12)
<br> at next (/home/agam/projects/AMEX/node_modules/express/lib/router/index.js:275:10)
<br> at Function.handle (/home/agam/projects/AMEX/node_modules/express/lib/router/index.js:174:3)
<br> at router (/home/agam/projects/AMEX/node_modules/express/lib/router/index.js:47:12)
<br> at Layer.handle [as handle_request] (/home/agam/projects/AMEX/node_modules/express/lib/router/layer.js:95:5)
<br> at trim_prefix (/home/agam/projects/AMEX/node_modules/express/lib/router/index.js:317:13)
<br> at /home/agam/projects/AMEX/node_modules/express/lib/router/index.js:284:7
<br> at Function.process_params (/home/agam/projects/AMEX/node_modules/express/lib/router/index.js:335:12)
<br> at next (/home/agam/projects/AMEX/node_modules/express/lib/router/index.js:275:10)
</pre>
</body>
</html>
this is the error i'm getting when I'm trying to debug the request received over the post request on node side.
And how to receive the file over back end??
var csv = require('fast-csv');
var mongoose = require('mongoose');
var Bulletin = require('../models/bulletin');
var multer = require("multer");
module.exports = (router) => {
router.post('/fileupload', (req, res) => {
console.log('### ' + req);
res.send(req.files);
});
/*router.post('/fileupload', multer({dest: "./uploads/"}).array("uploads", 12), function(req, res) {
res.send(req.files);
});*/
return router;
};
This is the nodejs code
1","Fed official says weak data caused by weather, should not slow taper","http://www.latimes.com/business/money/la-fi-mo-federal-reserve-plosser-stimulus-economy-20140310,0,1312750.story\?track=rss","Los Angeles Times","b","www.latimes.com","1394470370698
2","Fed's Charles Plosser sees high bar for change in pace of tapering","http://www.livemint.com/Politics/H2EvwJSK2VE6OF7iK1g3PP/Feds-Charles-Plosser-sees-high-bar-for-change-in-pace-of-ta.html","Livemint","b","www.livemint.com","1394470371207
3","US open: Stocks fall after Fed official hints at accelerated tapering","http://www.ifamagazine.com/news/us-open-stocks-fall-after-fed-official-hints-at-accelerated-tapering-294436","IFA Magazine","b","www.ifamagazine.com","1394470371550
4","Fed risks falling 'behind the curve', Charles Plosser says","http://www.ifamagazine.com/news/fed-risks-falling-behind-the-curve-charles-plosser-says-294430","IFA Magazine","b","www.ifamagazine.com","1394470371793
5","Fed's Plosser: Nasty Weather Has Curbed Job Growth","http://www.moneynews.com/Economy/federal-reserve-charles-plosser-weather-job-growth/2014/03/10/id/557011","Moneynews","b","www.moneynews.com","1394470372027
6","Plosser: Fed May Have to Accelerate Tapering Pace","http://www.nasdaq.com/article/plosser-fed-may-have-to-accelerate-tapering-pace-20140310-00371","NASDAQ","b","www.nasdaq.com","1394470372212
This is my sample csv file
> amex#1.0.0 start /home/agam/projects/AMEX
> node index.js
(node:6478) DeprecationWarning: current URL string parser is deprecated, and will be removed in a future version. To use the new parser, pass option { useNewUrlParser: true } to MongoClient.connect.
listening on port 3000
Conected to database: amex
### [object Object]
this is the thing it is printing
// 1) Express for nodejs
const express = require('express');
const app = express();
const template = require('./routes/template');
const upload = require('./routes/upload');
const path = require('path');
// const fileUpload = require('express-fileupload');
const router = express.Router();
const fu = require('./routes/fileUpload')(router);
//const carts = require('./routes/carts')(router);
// 5) body parser
const bodyParser = require('body-parser');
const cors = require('cors'); // CORS is a node.js package for providing a Connect/Express middleware that can be used to enable CORS with various options.
// 2) Mongodb connection
var mongoose = require('mongoose');
const config = require('./config/database');
mongoose.connect(config.uri, (err) => {
if (err) {
console.log('error ' + err);
} else {
console.log('Conected to database: ' + config.db);
}
});
app.use(cors({ origin: 'http://localhost:4200' }));
// 5) put bodyparser code before the routes (converts request to req.body)
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS');
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
// 3) connect node.js to angular
app.use(express.static(__dirname + '/client/dist/'));
// app.use(fileUpload());
app.use('/fu', fu); // Use Product routes in application
//app.use('/carts', carts);
//
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname + '/client/dist/index.html'));
});
app.listen(3000, () => {
console.log('listening on port 3000');
});
this is my index.js file which you are expecting
Hi I have done a few changes in your StackBiltz example, so I'm able to achieve the payload to be posted via a post call to in the form of JSON array and created a service which accepts this payload and on subscription passes the payload to your node post api
Created a function named pass
test.component.ts
import { Component, OnInit } from '#angular/core';
import { Router } from "#angular/router";
import { FileUtil } from './file.util';
import { Constants } from './test.constants';
import {TestService} from './test.service';
#Component({
selector: 'app-test',
templateUrl: './test.component.html',
styleUrls: ['./test.component.css']
})
export class TestComponent implements OnInit {
fileImportInput: any;
data;
csvRecords:any = [];
constructor(private _router: Router,
private _fileUtil: FileUtil,private testService:TestService
) { }
ngOnInit() {
}
// METHOD CALLED WHEN CSV FILE IS IMPORTED
fileChangeListener($event): void {
var text = [];
var target = $event.target || $event.srcElement;
var files = target.files;
if (Constants.validateHeaderAndRecordLengthFlag) {
if (!this._fileUtil.isCSVFile(files[0])) {
alert("Please import valid .csv file.");
this.fileReset();
}
}
var input = $event.target;
var reader = new FileReader();
reader.readAsText(input.files[0]);
reader.onload = (data) => {
let csvData = reader.result;
let csvRecordsArray = csvData.split(/\r\n|\n/);
var headerLength = -1;
if (Constants.isHeaderPresentFlag) {
let headersRow = this._fileUtil.getHeaderArray(csvRecordsArray, Constants.tokenDelimeter);
headerLength = headersRow.length;
}
this.csvRecords = this._fileUtil.getDataRecordsArrayFromCSVFile(csvRecordsArray,
headerLength, Constants.validateHeaderAndRecordLengthFlag, Constants.tokenDelimeter);
this.passThisFunction(this.csvRecords);
console.log('this.csvRecords',this.csvRecords);
if (this.csvRecords == null) {
//If control reached here it means csv file contains error, reset file.
this.fileReset();
}
}
reader.onerror = function () {
alert('Unable to read ' + input.files[0]);
};
};
fileReset() {
this.fileImportInput.nativeElement.value = "";
this.csvRecords = [];
}
passThisFunction(records:any){
console.log('inside function call');
console.log('records',records);
let json=JSON.stringify(records);
console.log('jsonified',json);
var str = json.replace(/},/g, "},\r\n");
console.log('das' + str);
this.testService.postCsvToMongo(str).subscribe((data) => {
console.log(data);
});
}
}
test.component.html
<h4>Read .CSV File using Angular2, JQuery and Bootstrap</h4>
<table>
<tr>
<td>
<input type="file"
#fileImportInput
name="File Upload"
id="txtFileUpload"
class="btn btn-primary"
(change)="fileChangeListener($event)"
accept=".csv"
/>
</td>
</tr>
</table>
<div>
<table class="table table-responsive table-hover" border="1"
style="width : 50%;">
<tbody>
<ng-container >
<tr >
<td *ngFor="let csvRec of csvRecords;let i=index">{{csvRec}}</td>
</tr>
</ng-container>
</tbody>
</table>
</div>
file.util.ts
import { Injectable } from '#angular/core';
#Injectable()
export class FileUtil {
constructor() {}
isCSVFile(file) {
return file.name.endsWith(".csv");
}
getHeaderArray(csvRecordsArr, tokenDelimeter) {
let headers = csvRecordsArr[0].split(tokenDelimeter);
let headerArray = [];
for (let j = 0; j < headers.length; j++) {
headerArray.push(headers[j]);
}
return headerArray;
}
validateHeaders(origHeaders, fileHeaaders) {
if (origHeaders.length != fileHeaaders.length) {
return false;
}
var fileHeaderMatchFlag = true;
for (let j = 0; j < origHeaders.length; j++) {
if (origHeaders[j] != fileHeaaders[j]) {
fileHeaderMatchFlag = false;
break;
}
}
return fileHeaderMatchFlag;
}
getDataRecordsArrayFromCSVFile(csvRecordsArray, headerLength,
validateHeaderAndRecordLengthFlag, tokenDelimeter) {
console.log(csvRecordsArray);
console.log(headerLength);
console.log(validateHeaderAndRecordLengthFlag);
console.log(tokenDelimeter);
var dataArr:any = [];
for (let i = 0; i < csvRecordsArray.length; i++) {
let data = csvRecordsArray[i].split(tokenDelimeter);
if(validateHeaderAndRecordLengthFlag && data.length != headerLength){
if(data==""){
alert("Extra blank line is present at line number "+i+", please remove it.");
return null;
}else{
alert("Record at line number "+i+" contain "+data.length+" tokens, and is not matching with header length of :"+headerLength);
return null;
}
}
dataArr=data;
console.log('dataArr',dataArr);
}
return dataArr;
}
}
test.service.ts
import{Injectable} from '#angular/core';
import{Http} from '#angular/http';
import {map} from 'rxjs/operators';
#Injectable()
export class TestService{
API_URL:string='your api url';
constructor(private http:Http){}
postCsvToMongo(csvJson:any){
return this.http.post(this.API_URL,csvJson)
.pipe(map((res)=>console.log(res)));
}
}
Related
Im currently trying to learn more about Express and Express-Validator but currently facing the following issue: When I'm starting the server and using Postman do excess one of the endpoints the response is not completed. However, it seems like the Validation Chain is being processed but afterwards nothing happens. I have the following modules:
index.ts
import {config} from 'dotenv';
import App from './api/app';
import ControlAmbilight from './api/routes/controlAmbilight';
import AdjustLightning from './app/AdjustLightning';
const ENV_FILE = path.join(__dirname, '..', '.env');
config({path: ENV_FILE});
const PORT = process.env.port || process.env.PORT || 3000;
const ambient = new AdjustLightning();
const app = new App([
new ControlAmbilight(ambient),
], <number> PORT);
app.listen();
app.ts
import * as bodyParser from 'body-parser';
import pino from 'pino';
import expressPino from 'express-pino-logger';
import errorMiddleware from './middleware/errorMiddleware';
export default class App {
private logger: pino.Logger;
private expressLogger;
public app: express.Application;
public port: number;
constructor(controllers: any, port: number) {
this.app = express();
this.port = port;
this.logger = pino({level: process.env.LOG_LEVEL || 'info'});
this.expressLogger = expressPino({logger: this.logger});
this.initializeMiddlewares();
this.initializeControllers(controllers);
this.initializeErrorHandling();
}
private initializeMiddlewares() {
this.app.use(this.expressLogger);
this.app.use(bodyParser.json());
}
private initializeControllers(controllers: any) {
controllers.forEach((controller: any) => {
this.app.use('/', controller.router);
});
}
private initializeErrorHandling() {
this.app.use(errorMiddleware)
}
public listen() {
this.app.listen(this.port, () => {
this.logger.info(`Server running on ${this.port}`);
});
}
}
controlAmbilight.ts
import {Router, Request, Response, NextFunction} from 'express';
import {ValidationChain, check, validationResult} from 'express-validator';
import AdjustLightning from '../../app/AdjustLightning';
// eslint-disable-next-line require-jsdoc
export default class ControlAmbilight {
private ambient: AdjustLightning;
// eslint-disable-next-line new-cap
public router = Router();
public path = '/controlAmbilight';
// eslint-disable-next-line require-jsdoc
constructor(ambient: AdjustLightning) {
this.ambient = ambient;
this.initializeRoutes();
}
// eslint-disable-next-line require-jsdoc
public initializeRoutes() {
this.router.post(this.path, this.controlValidator, this.setAmbilight.bind(this));
}
private controlValidator = (): ValidationChain[] => [
check('on').notEmpty().withMessage('Field \'on\' is required'),
check('on').isBoolean().withMessage('Field \'on\' must be type boolean'),
];
// eslint-disable-next-line require-jsdoc
private setAmbilight(req: Request, res: Response): void {
const errors = validationResult(req);
if (!errors.isEmpty()) {
res.status(422).json({error: errors.array()});
} else {
const isOn = (req.body.on == 'true');
res.send(`The curent state is: ${this.ambient.getIsActive()}`);
}
}
}
I was hopping that someone could explain me what I'm missing here. It seems like I need to call express` next() middleware function, but I'm not sure where to implement it.
EDIT
As requested I'm adding the errorMiddleware:
import { NextFunction, Request, Response } from 'express';
import HttpException from '../exceptions/HttpException';
export default function errorMiddleware (error: HttpException,
request: Request, response: Response, next: NextFunction) {
const status = error.status || 500;
const message = error.message || 'Ups... This did not work :(';
response
.status(status)
.send({ status,
message });
}
And as an additional comment: When I'm adding the Validation Chain directly into the post method within controlAmbilight.ts like that:
public initializeRoutes() {
this.router.post(this.path, [
check('on').notEmpty().withMessage('Field \'on\' is required'),
check('on').isBoolean().withMessage('Field \'on\' must be type boolean'),
], this.setAmbilight.bind(this));
}
It is working as expected.
I have built my portfolio webpage with next.js now I need to test it. to test the express server I use supertest. But the problem is I need to refactor express to use it. Because supertest need to access to app() before listening.
I started the way how I used to implement in node.js app. Put the express code in app.js and call it in index.js.
const express = require("express");
const server = express();
const authService = require("./services/auth");
const bodyParser = require("body-parser");
//put all the middlewares here
module.exports = server;
and then in index.js
const server = require("express")();
// const { parse } = require("url");
const next = require("next");
const routes = require("../routes");
const path = require("path");
require("./mongodb");
const dev = process.env.NODE_ENV !== "production";
const app = next({ dev });
// const handle = app.getRequestHandler(); //this is built in next route handler
const handle = routes.getRequestHandler(app);
app
.prepare()
.then(() => {
const server = require("./app");
//I required this outside too but it did not solve the issue
server.listen(3000, (err) => {
if (err) throw err;
console.log("> Ready on http://localhost:3000");
});
})
.catch((ex) => {
console.error(ex.stack);
process.exit(1);
});
with this set up, express is listening, I am able connect to mongodb, during the start up there is no issue.
When i request to localhost:3000, there is no response from localhost, it is spinning till timeout
Create a test client:
// test-client.ts
import { createServer, RequestListener } from "http";
import { NextApiHandler } from "next";
import { apiResolver } from "next/dist/next-server/server/api-utils";
import request from "supertest";
export const testClient = (handler: NextApiHandler) => {
const listener: RequestListener = (req, res) => {
return apiResolver(
req,
res,
undefined,
handler,
{
previewModeEncryptionKey: "",
previewModeId: "",
previewModeSigningKey: "",
},
false
);
};
return request(createServer(listener));
};
Test your APIs with:
// user.test.ts
import viewerApiHandler from "../api/user";
import { testClient } from "../utils/test-client";
const request = testClient(viewerApiHandler);
describe("/user", () => {
it("should return current user", async () => {
const res = await request.get("/user");
expect(res.status).toBe(200);
expect(res.body).toStrictEqual({ name: "Jane Doe" });
});
});
For those who want to add query parameters, here's the answer:
import { createServer, RequestListener } from 'http'
import { NextApiHandler } from 'next'
import { apiResolver } from 'next/dist/server/api-utils/node'
import request from 'supertest'
export const handlerRequester = (handler: NextApiHandler) => {
const listener: RequestListener = (req, res) => {
let query = {}
let queryUrl = req.url.split('?')[1]
if (queryUrl) {
queryUrl
.split('&')
.map((p) => [p.split('=')[0], p.split('=')[1]])
.forEach((k) => {
query[k[0]] = k[1]
})
}
return apiResolver(
req,
res,
query,
handler,
{
previewModeEncryptionKey: '',
previewModeId: '',
previewModeSigningKey: '',
},
false
)
}
const server = createServer(listener)
return [request(server), server]
}
I've just released a new npm package which handle this case here:
https://www.npmjs.com/package/nextjs-http-supertest
Feel free to test it and give me feedback !
am using angular 6 and express when am developing this api on authentcate uri it returning Http failure response for http://localhost:3000/api/authenticate: 404 Not Found
i have tried removing of the responses on my user.controller.js but the problem persisits it seems am missing out some point here and i dont know here it is at first i got an error saaying cant send headers after they are sent and the error was on my user.controller.js on this line else return res.status(404).json(info);
Here is my user.controller.js
const mongoose = require('mongoose');
const User = mongoose.model('User');
const passport = require('passport');
const _ = require('lodash');
module.exports.register = (req,res, next) => {
const user = new User();
user.fullname = req.body.fullname;
user.email = req.body.email;
user.College = req.body.College;
user.Department = req.body.Department;
user.password = req.body.password;
user.admintype = req.body.admintype;
user.save((err, doc) => {
if(!err) { res.send(doc)}
else
{
if(err.code == 11000)
res.status(422).send(['Duplicate email Address Found.'])
else
return next(err);
}
})
}
module.exports.authenticate = (req, res, next ) => {
//calll for passport authentication
passport.authenticate('local', (err, user, info) => {
//error form paasport middleware
if(err) return res.status(400).json(err);
//registered user
else if (user) return res.status(200).json({ "token":user.generateJwt() });
//unknown user or wrong password
else return res.status(404).json(info);
})(req, res);
}
module.exports.userProfile = (req, res, next) =>{
User.findOne({ _id:req._id},
(err,user) =>{
if(!user)
return res.status(404).json({ status: false, message : 'User Record not Found. '});
else
return res.status(200).json({ status:true , user : _.pick(user, ['fullname','email','university','College','Department','admintype'])});
} );
}
Here is my user.service.ts
```import { Injectable } from '#angular/core';
import { User } from './user.model';
import{ HttpClient, HttpHeaders } from '#angular/common/http';
import{ environment } from '../../environments/environment';
import { from } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class UserService {
selectedUser: User = {
fullname:'',
email:'',
university:'',
College:'',
Department:'',
password:'',
admintype:''
};
constructor(private http: HttpClient) { }
postUser(user:User)
{
return this.http.post(environment.apiBaseUrl+ '/register' ,user)
}
login(authCredentials)
{
return this.http.post(environment.apiBaseUrl+ '/authenticate',authCredentials);
}
setToken(token:string)
{
localStorage.setItem('token',token);
}
}```
Here is my sign-in.components.ts
```import { Component, OnInit } from '#angular/core';
import { NgForm } from '#angular/forms';
import { UserService } from 'src/app/shared/user.service';
import { Router } from '#angular/router';
#Component({
selector: 'app-sign-in',
templateUrl: './sign-in.component.html',
styleUrls: ['./sign-in.component.css']
})
export class SignInComponent implements OnInit {
constructor( private userService:UserService, private router:Router) { }
model = {
email:'',
password:''
};
emailRegex = /^(([^<>()\[\]\\.,;:\s#"]+(\.[^<>()\[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
serverErrorMessages : string;
ngOnInit() {
}
onSubmit(form :NgForm)
{
this.userService.login(form.value).subscribe(
res =>{
this.userService.setToken(res['token']);
this.router.navigateByUrl('/signup');
},
err =>{
this.serverErrorMessages = err.message;
});
}
}```
Here is my environment.ts
```/ This file can be replaced during build by using the `fileReplacements` array.
// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment = {
production: false,
apiBaseUrl:'http://localhost:3000/api'
};
/*
* For easier debugging in development mode, you can import the following file
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
*
* This import should be commented out in production mode because it will have a negative impact
* on performance if an error is thrown.
*/
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.```
Here is my auth.js
```const router = require('express').Router();
const User = require('../controller/model/User');
const ctrlUser = require('../controller/user.controller');
const jwthelper = require('../jwtHelper')
//validation
router.post('/register', ctrlUser.register);
router.post('/authenticate',ctrlUser.authenticate);
router.get('/userProfile',jwthelper.verifyJwtToken,ctrlUser.userProfile);
module.exports = router;```
Here is my index.js
```const express = require('express');
const app = express();
const mongoose = require('mongoose');
const dotenv = require('dotenv');
const cors = require('cors')
const bodyParser = require('body-parser');
require('./passportConfig');
const passport = require('passport');
dotenv.config();
//connect to mongodb
mongoose.set('useFindAndModify', false); mongoose.set('useUnifiedTopology', true);
mongoose.connect(process.env.DB_CONNECT,{ useNewUrlParser:true} , () =>
console.log('connected to db!')
);
//import routes
const authRoute = require('./routes/auth');
//middleware
app.use(bodyParser.json());
app.use(cors());
app.use(passport.initialize());
//error handler
app.use((err, req, res, next) =>{
if(err.name =='ValidationError')
{
var valErrs = [];
Object.keys(err.errors).forEach(key => valErrs.push(err.errors[key].message));
res.status(422).send(valErrs);
next();
}
});
//route middleware
app.use('/api',authRoute);
app.listen(3000, () => console.log("server Up and Running"));```
Any Help please on this one please thank you all
The only thing which is remain is to attach the router which you have define in the auth.js file to your express app like this
const authRouter = require('./auth');
And to prefix all routes define in the auth.js file you attach it as a middleware which is trigger on route prifix with \api
const express = require('express');
const app = express();
// define all your middleware and all other route
// and here you attach the auth router
app.use('\api', authRouter);
This will make authentication available on url http://localhost:3000/api/authenticate
You may also get 404 because of this line in authenticate route (by the way I think this must be a 400 - bad request, not 404, which is making confusion.)
else return res.status(404).json(info);
So to understand this, can you replace your authenticate route like this, and see what logs in the api console:
module.exports.authenticate = (req, res, next ) => {
console.log("req.body: ", req.body)
//calll for passport authentication
passport.authenticate('local', (err, user, info) => {
//error form paasport middleware
if(err) return res.status(400).json(err);
//registered user
else if (user) return res.status(200).json({ "token":user.generateJwt() });
//unknown user or wrong password
else {
console.log("info: ", info)
return res.status(400).json(info);
}
})(req, res);
Also it the angular component, can you change your onSubmit like this for easy debug:
be sure your form.value is correct:
onSubmit(form :NgForm)
{
console.log("form.value: ", form.value);
this.userService.login(form.value).subscribe(
res =>{
this.userService.setToken(res['token']);
this.router.navigateByUrl('/signup');
},
err =>{
console.log("err: ", err.message)
this.serverErrorMessages = err.message;
});
}
I am trying to upload a single file, and store it in a folder in my app directory. On the frontend, I am successfully selecting a file and adding it to the state of the Uploader component. When I make the POST request to the Node route with Axios, I am getting undefined in the request body.
Here is the component:
import React, { Component } from 'react';
import axios from 'axios';
export default class SandboxGet extends Component {
constructor(props) {
super(props);
this.state = {
file: null
};
}
fileSelectHandler = event => {
const file = event.target.files[0];
this.setState({ file: file.name });
};
fileUploadHandler = () => {
const data = new FormData();
data.append('file', this.state.file);
console.log(data);
axios
.post('http://localhost:4000/upload/', this.state.file, {
// receive two parameter endpoint url ,form data
})
.then(res => {
// then print response status
console.log(res.statusText);
});
};
render() {
return (
<div className="uploaderContainer">
<div className="uploadInput">
<i className="fas fa-upload" />
<button className="uploadBtn">Select Files</button>
<input type="file" name="file" onChange={this.fileSelectHandler} />
<div className="fileName">{this.state.fileName}</div>
</div>
<button className="uploadFile" onClick={this.fileUploadHandler}>
Upload!
</button>
</div>
);
}
}
Here is the Node server:
const express = require('express');
const app = express();
const multer = require('multer');
const cors = require('cors');
app.use(cors());
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, '/storage');
},
filename: (req, file, cb) => {
cb(null, Date.now() + '-' + file.originalname);
}
});
const upload = multer({ storage: storage }).single('file');
app.post('/upload', (req, res) => {
console.log(req.file); // => returns nothing
console.log(req.body; // => returns nothing
upload(req, res, function(err) {
if (err instanceof multer.MulterError) {
return res.status(500).json(err);
} else if (err) {
return res.status(500).json(err);
}
return res.status(200).send(req.file);
});
});
app.listen(4000, function() {
console.log('App running on port 3000');
});
I feel like I am getting close, but I am missing a big piece of the puzzle.
You are sending the this.state.file. You need to send the FormData.
In your case it is .post('http://localhost:4000/upload/', data)
Also, you need to send the multipart/form-data header.
const headers = {
'content-type': 'multipart/form-data'
}
Then,
axios.post('http://localhost:4000/upload/', data, {headers});
I have an Angular 6 app that uploads one file using POST:
submit-form.component.html
<input type="file" id="file" (change)="onFileChange($event.target.files)">
<button (click)="uploadFile()"> Upload </button>
submit-form.component.ts
import { Component, OnInit } from '#angular/core';
import { HttpResponse, HttpEventType } from '#angular/common/http';
import { FileService } from '../file.service';
#Component({
selector: 'app-submit-form',
templateUrl: './submit-form.component.html',
styleUrls: ['./submit-form.component.css']
})
export class SubmitFormComponent implements OnInit {
constructor( private fileService: FileService) { }
file_to_upload: File;
onFileChange(files: FileList) {
this.file_to_upload = files.item(0);
}
uploadFile() {
this.fileService.uploadFile(this.file_to_upload).subscribe(
(event) => {
if (event.type == HttpEventType.UploadProgress) {
const percentDone = Math.round(100 * event.loaded / event.total);
console.log(`Uploading - %${percentDone}...`);
} else if (event instanceof HttpResponse) {
console.log('File completely loaded!');
}
},
(err) => {
console.log('Upload Error', JSON.stringify(err));
}, () => {
console.log('Done uploading file!');
}
);
}
}
file.service.ts
import { Injectable } from '#angular/core';
import { HttpClient, HttpParams, HttpRequest } from '#angular/common/http';
import { Observable } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class FileService {
private uploadUrl = 'http://localhost:4200/upload';
constructor(private http: HttpClient) { }
uploadFile(file: File): Observable<any> {
var formData = new FormData();
formData.append('file_to_upload', file, file.name);
var params = new HttpParams();
var httpOptions = {
params: params,
reportProgress: true
};
const req = new HttpRequest('POST', this.uploadUrl, formData, httpOptions);
return this.http.request(req);
}
}
And Node.JS server that saves that file using multer:
server.js
const http = require('http')
const url = require('url')
const path = require('path')
const multer = require('multer')
const express = require('express')
const router = express()
const serverPort = 4200;
router.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
next();
});
var storage = multer.diskStorage({
destination: (req, file, cb) => cb(null, 'data/upload/'),
filename: (req, file, cb) => {
console.log('Upload file ', file)
cb(null, file.originalname)
}
});
var upload = multer({storage: storage});
router.post('/upload', upload.single('file_to_upload'), (req, res) => {
res.json({'Code': 200, 'Message': 'Success'});
});
router.listen(serverPort, () => console.log(`Server running at http://localhost:${serverPort}/`));
On Mozilla it works perfectly with small files (<500MB), but when I upload something bigger, the browser freezes between Uploading - 100% and File completely loaded!, rapidly raises it's memory consumption by approximately 1.5x file size and then instantly drops back down, outputting File completely loaded! and Done uploading file! (judging by data/upload/ folder, the file finishes uploading somewhere at the beginning of a memory spike). On Chrome file size does not matter - it always works properly (even with >3GB files).
If I use HTML's <form> for file upload, like this:
<form action="http://localhost:4200/upload" method="POST" enctype="multipart/form-data">
<input type="file" name="file_to_upload" >
<input type="submit" value="Upload">
</form>
...then in both browsers there are no sudden memory spikes. So, what's going on?