I spend the last 3 days to fix the problem , but i didnt figure out yet the issue.
Angular CLI: 6.0.8
Node: 8.11.2
OS: win32 x64
Angular: 6.0.6
multer. 1.3.1
my code at "childApi" using multer staff :
var store = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, './uploads');
},
filename: function (req, file, cb) {
cb(null, Date.now() + '.' + file.originalname);
}
});
var upload = multer({ storage: store , }).single('file');
router.post('/upload', function (req, res, next) {
upload(req, res, function (err) {
if (err) {
return console.log ('not working well')
}
//do all database record saving activity
return res.json({ originalname: req.file.originalname, uploadname: req.file.filename });
});
});
my code at "add-child" component using simple code :
import { Component, OnInit, Inject } from '#angular/core';
import { ActivatedRoute, Router } from '#angular/router';
import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '#angular/material';
import { Child } from '../../models/child';
import { ChildService } from '../../services/child.service';
import {FileUploader } from 'ng2-file-upload';
const uri = 'http://localhost:3000/childApi/upload';
#Component({
selector: 'app-add-child',
templateUrl: './add-child.component.html',
styleUrls: ['./add-child.component.css']
})
export class AddChildComponent implements OnInit {
newChild = new Child;
uploader: FileUploader = new FileUploader({ url: uri });
attachmentList: any = [];
constructor(private childService: ChildService,
private route: ActivatedRoute,
private router: Router,
public dialogRef: MatDialogRef<AddChildComponent>,
#Inject(MAT_DIALOG_DATA) public data: any) {
this.uploader.onCompleteItem = (item: any, response: any, status: any, headers: any) => {
this.attachmentList.push(JSON.parse(response));
};
}
The problem is that after I upload the file to the folder "uploads"
,I want to display my new photo on the screen.
The console give me this error :
GET unsafe:C:\fakepath\child+thinking.jpg 0 ()
If someone help its will be amazing.
Thanks...
I figure out what to do , I just put this sentences inside my code at "add-child" component using :
this.uploader.onCompleteItem = (item: any, response: any, status: any, headers: any) => {
this.newChild.user_img = JSON.parse(response).uploadname;
this.attachmentList.push(JSON.parse(response));
};
}
As I understand from your post that you have doing a model named child inside your project so if you have can I take a look on it I will be grateful because I'm doing the same task except still getting the error:
Access to XMLHttpRequest at 'http://localhost:4000/file/upload' from origin 'http://localhost:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
core.js:1449 ERROR SyntaxError: Unexpected end of JSON input
at JSON.parse (<anonymous>)
at FileUploader.UploadFileComponent.uploader.onCompleteItem (upload-file.component.ts:27)
at FileUploader.push../node_modules/ng2-file-upload/file-upload/file-uploader.class.js.FileUploader._onCompleteItem (file-uploader.class.js:199)
at XMLHttpRequest.xhr.onerror [as __zone_symbol__ON_PROPERTYerror] (file-uploader.class.js:268)`
javascript html typescript angular6 multer
Related
I have followed the documentation of "Advanced usage" https://v3.nuxtjs.org/guide/features/server-routes#advanced-usage-examples
Now i tried it out:
My folder is structured like this:
server\api\global.ts
Here is my file global.ts
import { createRouter } from "h3";
const router = createRouter();
router.get("/", () => "Hello World");
export default router;
Now i try to fetch some data:
export const Bloggy = {
login({ password, username }: LoginParameterI) {
return $fetch("/api/global", {
method: "GET",
});
},
};
interface LoginParameterI {
password: string;
username: string;
}
Now when i try to fetch some data, i receive an error:
[nuxt] [request error] Invalid lazy handler result. It should be a function
I wanted to use it with router, because i want to use certain middlewares for certain routes. In the nuxt documentation the middlewares will get triggered on every route
What am i doing wrong?
I try to use a NestJS backend with a Nginx reverse proxy.
I have coded an authentication part in my NestJS backend.
My problem is that when I used my frontend / backend in local mode, all is ok.
When I use it through Nginx, I always retrieve a 401 error.
I think it’s due to the LocalStrategy in NestJS
Here is the part in the local.strategy.ts file
import { Strategy } from 'passport-local';
import { PassportStrategy } from '#nestjs/passport';
import { Injectable, UnauthorizedException } from '#nestjs/common';
import { AuthService } from '../auth.service';
#Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
constructor(private authService: AuthService) {
super({
usernameField: 'userLogin',
passwordField: 'userPassword',
});
}
async validate(userLogin: string, userPassword: string): Promise<any> {
const user = await this.authService.validateUser(userLogin, userPassword);
if (!user) {
throw new UnauthorizedException();
}
return user;
}
}
Here is the part in the app.controller.ts file
#Public()
#UseGuards(LocalAuthGuard)
#Post('auth/login')
async login(#Request() req) {
return this.authService.login(req.user);
}
But I don’t know how to change it.
If somebody have an example it build be great.
Thanks in advance.
I've tested with curl locally on server (without nginx).
I saw my error (mysql access to test the user), it was not due to nginx configuration.
Currently in development it works just fine... localhost:4200 for the front-end and localhost:8080 for the back-end
However, I just deployed it and the front-end get displayed, but isn't getting the data from the API because in my app.service.ts I'm doing the following:
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Injectable({
providedIn: 'root'
})
export class ApiService {
private apiUrl = 'http://localhost:8080/api'
constructor(private http: HttpClient) { }
public getNews() {
return this.http.get(`${this.apiUrl}/countries`)
}
}
As you can see, I'm hardcoding the localhost:8080 and it works fine in development, but when it comes to production Heroku does not assign me the port 8080, it assigns me another one.
That being said... How can I tweak this in order to read the port Heroku gives me?
This is my app.js file
const express = require('express');
const app = express();
const scrapper = require('./backend/scrapper')
// Create link to Angular build directory
var distDir = __dirname + "/dist/covid19";
app.use(express.static(distDir));
app.use((req, res, next) => {
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept, Authorization"
);
res.setHeader(
"Access-Control-Allow-Methods",
"GET, POST, PATCH, PUT, DELETE, OPTIONS"
);
next();
});
app.use("/api/countries", async (req, res, next) => {
const data = await scrapper.getCountries()
res.status(200).json(data)
})
const port = process.env.PORT || 8080;
app.listen(port, () => {
console.log(`API listening on port ${port}...`);
});
module.exports = app;
As you can see I'm declaring my port to be process.env.PORT || 8080, but this is for the backend... How can achieve this but in my API call in the service.ts file?
You guys pointed me in the right direction, but to be precise:
I noticed that in Angular you get a environments folder with two files 1. environment.ts and environment.prod.ts.
I just had to make sure to use to point to the URL that Heroku gave me for my app after deploying yourappname.herokuapp.com, by doing the following in my environments.prod.ts (Which is the one that Heroku is gonna look for)
export const environment = {
production: true,
apiUrl: "https://yourappname.herokuapp.com/api"
};
And in my api.service.ts I ended up with the following code:
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { environment } from '../environments/environment'
const API_URL = environment.apiUrl;
#Injectable({
providedIn: 'root'
})
export class ApiService {
constructor(private http: HttpClient) { }
public getNews() {
return this.http.get(API_URL + '/countries')
}
}
When you deploy a web server on Heroku you bind to the $PORT Heroku tells you to bind to.
When you visit your deployed app you don't specify a port. You just connect to yourappname.heroku.com. The DNS automatically translates it into ipaddress:port.
So on your frontend you just point to yourappname.heroku.com instead of ipaddress:port.
I'm using NestJS, Node, and Express for my backend and Angular for my frontend. I have a stepper where the user steps through and enters in information about themselves as well as a profile photo and any photos of their art that they want to post (it's a rough draft). I'm sending the files to the backend with this code:
<h2>Upload Some Photos</h2>
<label for="singleFile">Upload file</label>
<input id="singleFile" type="file" [fileUploadInputFor]= "fileUploadQueue"/>
<br>
<mat-file-upload-queue #fileUploadQueue
[fileAlias]="'file'"
[httpUrl]="'http://localhost:3000/profile/artPhotos'">
<mat-file-upload [file]="file" [id]="i" *ngFor="let file of fileUploadQueue.files; let i = index"></mat-file-upload>
</mat-file-upload-queue>
The front-end sends the photos as an array of files; I tried to change it so that it just sent a single file but could not get it working. I'm less focused on that because the user may need to upload multiple files, so I want to figure it out regardless. On the backend, I'm using multer, multer-s3, and AWS-SDK to help upload the files however it isn't working. Here is the controller code:
#Post('/artPhotos')
#UseInterceptors(FilesInterceptor('file'))
async uploadArtPhotos(#Req() req, #Res() res): Promise<void> {
req.file = req.files[0];
delete req.files;
// tslint:disable-next-line:no-console
console.log(req);
await this._profileService.fileupload(req, res);
}
Here is ProfileService:
import { Profile } from './profile.entity';
import { InjectRepository } from '#nestjs/typeorm';
import { Repository } from 'typeorm';
import { ProfileDto } from './dto/profile.dto';
import { Req, Res, Injectable, UploadedFile } from '#nestjs/common';
import * as multer from 'multer';
import * as AWS from 'aws-sdk';
import * as multerS3 from 'multer-s3';
const AWS_S3_BUCKET_NAME = 'blah';
const s3 = new AWS.S3();
AWS.config.update({
accessKeyId: 'blah',
secretAccessKey: 'blah',
});
#Injectable()
export class ProfileService {
constructor(#InjectRepository(Profile)
private readonly profileRepository: Repository<Profile> ){}
async createProfile( profileDto: ProfileDto ): Promise<void> {
await this.profileRepository.save(profileDto);
}
async fileupload(#Req() req, #Res() res): Promise<void> {
try {
this.upload(req, res, error => {
if (error) {
// tslint:disable-next-line:no-console
console.log(error);
return res.status(404).json(`Failed to upload image file: ${error}`);
}
// tslint:disable-next-line:no-console
console.log('error');
return res.status(201).json(req.file);
});
} catch (error) {
// tslint:disable-next-line:no-console
console.log(error);
return res.status(500).json(`Failed to upload image file: ${error}`);
}
}
upload = multer({
storage: multerS3({
// tslint:disable-next-line:object-literal-shorthand
s3: s3,
bucket: AWS_S3_BUCKET_NAME,
acl: 'public-read',
// tslint:disable-next-line:object-literal-shorthand
key: (req, file, cb) => {
cb(null, `${Date.now().toString()} - ${file.originalname}`);
},
}),
}).array('upload', 1);
}
I haven't implemented any middleware extending multer, but I don't think I have to. You can see in the controller I erase the files property on req and replace it with the file where it's value is just the first member of the files array but that was just to see if it would work if I send it something it was expecting, but it did not work then. Does anyone have any ideas regarding how I can fix this? Or can anyone at least point me in the right direction with a link to a relevant tutorial or something?
My first guess would be that you are using the FileInterceptor and multer. I assume FileInterceptor adds multer in the controller which makes it available to the #UploadedFile decorator. Which could cause a conflict to your later use of multer. Try removing the interceptor and see if that fixes the issue.
Also I am attaching how I am doing file uploads. I am only uploading single images and I am using the AWS SDK so I don't have to work with multer directly, but here is how I am doing it, it might be helpful.
In the controller:
#Post(':id/uploadImage')
#UseInterceptors(FileInterceptor('file'))
public uploadImage(#Param() params: any, #UploadedFile() file: any): Promise<Property> {
return this.propertyService.addImage(params.id, file);
}
Then my service
/**
* Returns a promise with the URL string.
*
* #param file
*/
public uploadImage(file: any, urlKey: string): Promise<string> {
const params = {
Body: file.buffer,
Bucket: this.AWS_S3_BUCKET_NAME,
Key: urlKey
};
return this.s3
.putObject(params)
.promise()
.then(
data => {
return urlKey;
},
err => {
return err;
}
);
}
Thanks Jedediah, I like how simple your code is. I copied your code however it still wasn't working. Turns out you have to instantiate the s3 object after you update the config with your accesskey and secretID.
I have a nodejs as Backend Service and angular 5 as front end, I want to call a POST method to NodeJS Server, send a Blob File to server.
But that post method is executed, and nothing shown on backend console log.
Here are some code piece:
server.js
const express = require('express');
const app = express();
const port = process.env.PORT || 3000;
const bodyParser = require('body-parser');
const cors = require('cors');
app.use(cors());
//create a cors middleware
app.use(function (req, res, next) {
//set headers to allow cross origin request.
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();
});
app.post('/decrypt', function (req, res) {
console.log("decrypt called");
return 'data back';
})
And in AngularJS:
database.services.ts:
import { Injectable, Input } from '#angular/core';
import { Http, Response, ResponseContentType } from '#angular/http';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/do';
import { Observable } from 'rxjs/Observable';
import { saveAs, FileSaver } from 'file-saver/FileSaver';
import { HttpClientModule, HttpClient, HttpHeaders, HttpParams } from '#angular/common/http';
#Injectable()
export class DatabaseService {
private API_GET_LIST_FILES = 'http://localhost:3000/files';
private BASE_URL = 'http://localhost:3000/';
private API_GET_FILE = 'http://localhost:3000/download?name=';
constructor(private http: Http) { }
getFile(key: string) {
return this.http.get(this.API_GET_FILE + key, {
responseType: ResponseContentType.Blob
})
.map(res => {
return {
filename: key.split('/').pop(),
data: res.blob()
};
})
.subscribe(res => {
this.decryptFile(res.data);
saveAs(res.data, res.filename);
}, error => {
console.log('download error:', JSON.stringify(error));
}, () => {
console.log('Completed file download.');
})
}
decryptFile(data: any): Observable<any> {
const httpOptions = {
Headers: new HttpHeaders({
'Content-Type': 'application/octet-stream',
'data': data
})
};
console.log(data, typeof data, this.BASE_URL + `decrypt`);
return this.http.post(`http://localhost:3000/decrypt`, httpOptions);
}
}
This getFile function will be called once I click the file download button on page, because in the browser console, it will print out the Blob(564534) {size: 564534, type: "application/octet-stream"} "object" "http://localhost:3000/decrypt"
I want the nodejs Server to take this post method and the Blob (GPG file) object as a parameter, and do something.
But looks like the backend server didn't print out anything.
Please advise how to modify this code, should I use POST or PUT? I want to pass a GPG file to nodeJS server and decrypt it.
Thanks,
decryptFile returns Observable, you must subscribe to it to execute the http call:
this.decryptFile(res.data).subscribe(decrypted => {???});
Edit: Could not resist, a few observations, feel free to ignore:
Why do you get a file from server and then send it back there? Why don't you just decrypt the file during the first API call and return that?
From security point of view... nevermind, I just hope there will be some authorization on the server as you are dealing with PGP files...
You should preferably start using HttpClient from #angular/common/http instead of Http. Good news is you have that imported already.
Depending on the size of the files you might want to consider using http upload instead of POST. Still, see the first point.