Angular 5 - Node/Express - not able to download pdf - node.js

am trying to download pdf file from local folder that structures like
assets/test.pdf.
server.js
app.get('/ePoint', (req,res)=>{
// some dumb code :P
});
demo.ts
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { Headers } from '#angular/http';
import {Observable} from 'rxjs';
fileDownload() {
const headers = new HttpHeaders();
headers.append('Accept', 'application/pdf');
this._http.get('http://localhost:3000/ePoint', { headers: headers })
.toPromise()
.then(response => this.saveItToClient(response));
}
private saveItToClient(response: any) {
const contentDispositionHeader: string = response.headers.get('Content-Disposition');
const parts: string[] = contentDispositionHeader.split(';');
const filename = parts[1].split('=')[1];
const blob = new Blob([response._body], { type: 'application/pdf' });
saveAs(blob, filename);
}
i dont know where i did mistake. in browser network console. its shows 200 ok. but in normal browser console shows as below attachment
Note: i referred for ts file from here
helps much appreciated

try this...
component.ts
downloadDocument(documentId: string) {
this.downloadDocumentSubscription = this.getService.downloadScannedDocument(documentId).subscribe(
data => {
this.createImageFromBlob(data);
},
error => {
console.log("no image found");
$("#errorModal").modal('show'); //show download err modal
});
}
createImageFromBlob(image: Blob) {
console.log("mylog", image);
if (window.navigator.msSaveOrOpenBlob) // IE10+
window.navigator.msSaveOrOpenBlob(image, "download." + (image.type.substr(image.type.lastIndexOf('/') + 1)));
else {
var url = window.URL.createObjectURL(image);
window.open(url);
}
}
service.ts
downloadScannedDocument(documentId: string): Observable<any> {
let params = new HttpParams();
if (documentTypeParam == false)
params = params.set('onlyActive', 'false');
let fileResult: Observable<any> = this.http.get(`${this.apiBaseUrl}/${documentId}`, { responseType: "blob", params: params });
return fileResult;
}

Related

Vue - How to display image received from backend API?

I'm building a webApp in MEVN stack (Mongo, Express, Vue, Node).
In my backend, I have a controller (./backend/controllers/screenshots.controller.js) downloading an image from an external REST API. The image (PNG) is downloaded in a directory called 'images' placed in the controllers directory.
screenshots.controller.js:
const path = require('path');
const axios = require('axios');
const fs = require('fs');
const downloadScreenshot = async(screenshotPath) => {
let isDownloaded = false;
const fileUrl = `https://myexternalapi.com/screenshot/${screenshotPath}`;
const fileName = screenshotPath.split('/')[1]
const downloadFolder = './images'
if(!fs.existsSync(downloadFolder)){
fs.mkdirSync(downloadFolder);
console.log('Images directory created successfully.');
}
const localFilePath = path.resolve(__dirname, downloadFolder, fileName);
try {
const response = await axios({
method: 'GET',
url: fileUrl,
responseType: 'stream',
});
if(response.status === 200){
isDownloaded = true;
}
await response.data.pipe(fs.createWriteStream(localFilePath));
} catch (error) {
console.log('Error occured while downloading screenshot... : ', error);
}
return { isDownloaded, fileName };
}
const readScreenshot = async(req, res) => {
try {
const {isDownloaded, fileName} = await downloadScreenshot(req.body.temp);
if(isDownloaded){
console.log('__dirname + /images/ + fileName : ', __dirname + '/images/' + fileName )
res
.status(200)
.sendFile(fileName, {root : __dirname + '/images/'} );
} else {
res
.status(500)
.send({
message: 'No screenshot for this offer...'
})
}
} catch (error) {
console.log('Error occured while retrieving screenshot...', error)
res
.status(500)
.send({ message: error });
}
}
module.exports = {
readScreenshot: readScreenshot,
}
I would like to display the required image in my Vue app. Thus, I created the following view: ReadScreenshot.vue
<template>
<div>
<img :src="img">
</div>
</template>
<script>
import Screenshots from '../../services/screenshots.service'
export default {
props: ['id'],
data(){
return {
img: '',
}
},
async mounted(){
console.log(this.id)
const temp = await Screenshots.readScreenshot({ temp: this.id });
console.log(temp)
this.img = temp.data
}
}
</script>
Here is my screenshots.service.js script:
import api from '../../http-common';
export default new class ScreenshotsService {
//Read Screenshot
readScreenshot(screenshotName){
return api.post('read/screenshot', screenshotName)
}
}
Console.log(temp) is returning empty data.
In the screenshots.controller.js file, if I'm forcing the fileName with an existing one in the sendFile function, e.g. '2882LsgIXHOiXiOQ5MSv3R6v1hDijAdG5i756CdG5o7v527i5sS1XZgiXR6i1sSGj.png', I'm receiving a non empty data in my ReadScreenshot.vue .
Even if I'm receiving the data, the image is still not displayed...
How should I proceed, to get this right?
thks for your help

SvelteKit endpoint: converting from Node/Express

New to SvelteKit and working to adapt an endpoint from a Node/Express server to make it more generic so as to be able to take advantage of SvelteKit adapters. The endpoint downloads files stored in a database via node-postgresql.
My functional endpoint in Node/Express looks like this:
import stream from 'stream'
import db from '../utils/db'
export async function download(req, res) {
const _id = req.params.id
const sql = "SELECT _id, name, type, data FROM files WHERE _id = $1;"
const { rows } = await db.query(sql, [_id])
const file = rows[0]
const fileContents = Buffer.from(file.data, 'base64')
const readStream = new stream.PassThrough()
readStream.end(fileContents)
res.set('Content-disposition', `attachment; filename=${file.name}`)
res.set('Content-Type', file.type)
readStream.pipe(res)
}
Here's what I have for [filenum].json.ts in SvelteKit so far...
import stream from 'stream'
import db from '$lib/db'
export async function get({ params }): Promise<any> {
const { filenum } = params
const { rows } = await db.query('SELECT _id, name, type, data FROM files WHERE _id = $1;', [filenum])
if (rows) {
const file = rows[0]
const fileContents = Buffer.from(file.data, 'base64')
const readStream = new stream.PassThrough()
readStream.end(fileContents)
let body
readStream.pipe(body)
return {
headers: {
'Content-disposition': `attachment; filename=${file.name}`,
'Content-type': file.type
},
body
}
}
}
What is the correct way to do this with SvelteKit without creating a dependency on Node? Per SvelteKit's Endpoint docs,
We don't interact with the req/res objects you might be familiar with from Node's http module or frameworks like Express, because they're only available on certain platforms. Instead, SvelteKit translates the returned object into whatever's required by the platform you're deploying your app to.
UPDATE: The bug was fixed in SvelteKit. This is the updated code that works:
// src/routes/api/file/_file.controller.ts
import { query } from '../_db'
type GetFileResponse = (fileNumber: string) => Promise<{
headers: {
'Content-Disposition': string
'Content-Type': string
}
body: Uint8Array
status?: number
} | {
status: number
headers?: undefined
body?: undefined
}>
export const getFile: GetFileResponse = async (fileNumber: string) => {
const { rows } = await query(`SELECT _id, name, type, data FROM files WHERE _id = $1;`, [fileNumber])
if (rows) {
const file = rows[0]
return {
headers: {
'Content-Disposition': `attachment; filename="${file.name}"`,
'Content-Type': file.type
},
body: new Uint8Array(file.data)
}
} else return {
status: 404
}
}
and
// src/routes/api/file/[filenum].ts
import type { RequestHandler } from '#sveltejs/kit'
import { getFile } from './_file.controller'
export const get: RequestHandler = async ({ params }) => {
const { filenum } = params
const fileResponse = await getFile(filenum)
return fileResponse
}

Sending image from Nodejs to Angular

I'm trying to send an image from the server to the client. After some googling, the best solution seems to send the data as ArrayBuffer and then convert it to Blob on FE. However, I'm not able to get the image to display on FE. Am I doing some conversions wrong that might cause the issue?
For the server code:
exports.getImage = async (req, res) => {
try {
const file = fs.readFileSync(`${__dirname}/images/image.png`);
let ab = file.buffer.slice(file.byteOffset, file.byteOffset + file.byteLength);
return res.status(200).send(ab);
} catch (err) {
console.log(err);
return res.status(400).send({
code: err.errCode,
description: 'General error'
});
}
}
And on the receiving side in angular:
service.ts
getCustomMap(groupId: string, mapId: string): Observable<ArrayBuffer> {
return this._http
.get(`${this._URL}/${groupId}/customMap/${mapId}`, {
responseType: 'arraybuffer'
});
}
image component:
getCustomMap() {
this._groupManagerService.getCustomMap()
.subscribe((imgFile: ArrayBuffer) => {
map.file = new Blob([imgFile], { type: 'image/png' });
map.url = this.sanitizer.bypassSecurityTrustUrl(window.URL.createObjectURL(map.file));
});
}
Thank you
Simply follow the steps below:
1. Server / Node.js:
app.get('/', (req, res) => {
const imageName = "image.png"
const imagePath = path.join(__dirname, "images", imageName);
fs.exists(imagePath, exists => {
if (exists) {
const { size } = fs.statSync(imagePath);
res.writeHead(200, {
'Content-Type': 'image/png',
'Content-Length': size,
'Content-Disposition': `attachment; filename='${imageName}`
});
fs.createReadStream(imagePath).pipe(res);
}
else res.status(400).send('Error: Image does not exists');
});
})
Optionally: using sendFile as below:
app.get('/', (req, res) => {
const imageName = "image.jpg"
const imagePath = path.join(__dirname, "images", imageName);
fs.exists(imagePath, exists => {
if (exists) res.sendFile(imagePath);
else res.status(400).send('Error: Image does not exists');
});
});
2. Client / Angular - Component:
public url: SafeResourceUrl;
constructor(private http: HttpClient, private sanitizer: DomSanitizer) {
this.getImage('URL').subscribe(x => this.url = x)
}
public getImage(url: string): Observable<SafeResourceUrl> {
return this.http
.get(url, { responseType: 'blob' })
.pipe(
map(x => {
const urlToBlob = window.URL.createObjectURL(x) // get a URL for the blob
return this.sanitizer.bypassSecurityTrustResourceUrl(urlToBlob); // tell Anuglar to trust this value
}),
);
}
3. Client / Angular - Template:
<img [src]="url">

Send File from Angular to Nodejs - Cannot read property 'headers' of undefined

I'm having issues passing my data with a file to my nodejs backend. I'm currently using azure functions to run my nodejs code. Currently when I pass the data with file, I'm getting a Cannot read property 'headers' of undefined I'm adding the header in the options so I don't really understand why I'm getting the error.` Working with files is definitely one of my weaknesses so I appreciate any help!
import { Injectable, OnDestroy } from "#angular/core";
import { Subject, Observable } from "rxjs";
import {
HttpClient,
HttpParams,
HttpRequest,
HttpHeaders,
HttpEvent,
HttpEventType
} from "#angular/common/http";
import { map, takeUntil, switchMap } from "rxjs/operators";
import { Router } from "#angular/router";
import { environment } from 'src/environments/environment';
import { AuthService } from '../auth.service';
import { SendAppealModel } from './send-appeal.model';
#Injectable({ providedIn: "root" })
export class SubmitAppealService implements OnDestroy {
destroy = new Subject();
constructor(private http: HttpClient, private router: Router, private authService: AuthService) { }
ngOnDestroy() {
this.destroy.next();
this.destroy.complete();
}
submitAppeal(
username: string,
email: string,
file: File
) {
let form = new FormData();
form.append('file', file);
form.append('username', username);
form.append('email', email);
console.log("FILE OUTPUT");
console.log(file);
let headers = new HttpHeaders();
headers.append('Content-Type', 'multipart/form-data');
headers.append('Accept', 'application/json');
let options = { headers: headers, reportProgress: true };
const api = environment.azure_function_url + `/PATCH-Send-Appeal`;
const req = new HttpRequest('PATCH', api, form, options);
return this.http.request(req)
.pipe(
map((res: HttpEvent<any>) => {
if (res.type === HttpEventType.Response) {
return res.body.id.toString();
} else if (res.type === HttpEventType.UploadProgress) {
// Compute and show the % done:
const UploadProgress = +Math.round((100 * res.loaded) / res.total);
return UploadProgress;
}
})
);
}
}
azure function
const multer = require('multer');
const upload = multer({ dest: 'public/uploads/' }).single('file');
module.exports = function (context, req) {
context.log('JavaScript HTTP trigger function processed a request.');
upload();
console.log(req.file);
var filename = path.basename("../" + req.file.path);
console.log("filename");
console.log(req.file.destination);
console.log(__dirname);
var form = new formidable.IncomingForm();
console.log("form");
console.log(form);
context.res = {
status: 200,
headers: {
'Access-Control-Allow-Credentials': 'true',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'PATCH, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Set-Cookie',
'Access-Control-Max-Age': '86400',
Vary: 'Accept-Encoding, Origin',
'Content-Type': 'application/json',
},
};
context.done();
};
I'm assuming you are getting that error because your headers aren't actually making it to your azure function.
Currently you have this:
let headers = new HttpHeaders();
headers.append('Content-Type', 'multipart/form-data');
headers.append('Accept', 'application/json');
let options = { headers: headers, reportProgress: true };
You can't do that. headers.append doesn't do an in-place update. It returns a new HttpHeaders object. So, you actually need this:
let headers = new HttpHeaders();
headers = headers.append('Content-Type', 'multipart/form-data');
headers = headers.append('Accept', 'application/json');
let options = { headers: headers, reportProgress: true };
Per comments, I see one other thing that looks a little off to me. This may be part of the issue. Try updating your HTTP call to this:
const req = new HttpRequest('PATCH', api, form, options);
return this.http.patch(api, form, options)
.pipe(
map((res: HttpEvent<any>) => {
if (res.type === HttpEventType.Response) {
return res.body.id.toString();
} else if (res.type === HttpEventType.UploadProgress) {
// Compute and show the % done:
const UploadProgress = +Math.round((100 * res.loaded) / res.total);
return UploadProgress;
}
})
);
You might also set a breakpoint in your azure function on the first line to inspect the request object and make sure your HttpHeaders are making it in.

Angular2 / NodeJS file uploader. req.files not present

I am currently developing an application in Angular2 which is wrapped by in a NodeJS instance which talks to an API. I am currently implementing some file upload functionality and cannot get a function which captures the API file upload request in the NodeJS layer to show that it is catching the files. There is no 'files' property on the 'req' object.
Here is my code:
import { Component } from "#angular/core";
import { routes } from "../../../routes";
import { FilesService } from "../../../services/files.service";
#Component({
selector : 'file-upload',
moduleId : module.id,
templateUrl : '/app/views/files/file-upload.html',
})
export class FileUploaderDirective {
private _filesToUpload: Array<File> = [];
constructor(
private _filesService: FilesService
) {
}
fileChangeEvents(fileInput: any) {
this._filesToUpload = <Array<File>> fileInput.target.files;
}
upload() {
this._filesService.sendFile(routes.api.files, [], this._filesToUpload)
.then((result) => {
console.log(result);
}, (error) => {
console.log(error);
});
}
}
MY file upload Angular2 service:
import { Injectable } from "#angular/core";
import { Observable } from "rxjs";
#Injectable()
export class FilesService {
constructor() {
}
sendFile(url: String, vars: Array<String>, files: File[]): Promise<any> {
return new Promise((resolve, reject) => {
let formData: FormData = new FormData(),
xhr: XMLHttpRequest = new XMLHttpRequest();
for (let i = 0; i < files.length; i++) {
formData.append("uploads[]", files[i], files[i].name);
}
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
resolve(JSON.parse(xhr.response));
} else {
reject(xhr.response);
}
}
};
xhr.open('POST', url, true);
xhr.send(formData);
});
}
}
The NodeJS route that catches the api request and forwards it to a controller function in NodeJS:
router.post('/upload', function(req, res, next) {
filesRoutesControllerObjectInstance.upload(req, res, next);
});
And the function which is supposed to catch the request and send the files to the API:
var ApiBase_RequestLayer = require('../ApiBase_RequestLayer'),
Config = require(global.appRoot + '/Config'),
util = require('util');
function Files() {
Files.super_.call(this);
this.requestBaseUrl = Config.brain.url + '/upload';
}
Files.prototype.upload = function(req, res) {
if(req) {
}
};
util.inherits(Files, ApiBase_RequestLayer);
module.exports = Files;
When I debug the request there is no files present on the request when I debug the 'req' object in the NodeJS 'uoload' route and the controller. As you can see I am attempting to send them using the FormData Angular2 functionality. Can anyone see what I am doing wrong here.

Resources