failed to download file in angular 5 - angular5

I am trying to download a pdf file from an url in angular 5.It works nicely in html by adding the url and 'download' property to 'a' tag.When I trying to do the same in angular 5,but it's not working.
service.ts
pdfDownload() {
var options = new RequestOptions({responseType: ResponseContentType.Blob });
// Process the file downloaded
return this.http.get('https://.....test.pdf', options);
}
component.ts
this.rcycCommonService.pdfDownload().subscribe((res)=>{
this.saveFile(res.blob());
})
saveFile (blobContent) {
var blob = new Blob([blobContent], { type: 'application/pdf' });
saveAs(blob,"mypdf.pdf");
};
but it shows following errors.
ERROR in src/app/rcyc-services/rcyc-common/rcyc-common.service.ts(426,137): error TS2345: Argument of type 'RequestOptions' is not assignable to parameter of type '{ headers?: HttpHeaders | { [header: string]: string | string[]; }; observe?: "body"; params?: Ht...'.
Types of property 'headers' are incompatible.
Type 'Headers' is not assignable to type 'HttpHeaders | { [header: string]: string | string[]; }'.
Type 'Headers' is not assignable to type '{ [header: string]: string | string[]; }'.
Index signature is missing in type 'Headers'.
can any one post the correct code to download a file in angular 5?

this is a Typescript compilation error. As the error says, the second parameter of this.http.get should be a Headers, HttpHeaders, or string-indexed object (not a RequestOptions).

you can use axios , its butter to download file
you can install axios
npm i axios
and import in your service
import axios from 'axios';
return axios.get(`${URL}`,{headers , responseType:"blob"}).then(({ data }) => {
const downloadUrl = window.URL.createObjectURL(new Blob([data]));
const link = document.createElement('a');
link.href = downloadUrl;
link.setAttribute('download', `${fileName}.csv`); //any other extension and type you want
document.body.appendChild(link);
link.click();
link.remove();
});

Related

Front end Mutation call won't resolve

I have a react-native mobile app making a call to a graphql backend. Queries are working without issue. I just coded a mutation however, and everytime I try to make a call, the client is raising this issue:
Error: Could not find a default resolver for travelPassPasswordResetEmail. Please supply a resolver for this mutation.: {"response":{"errors":[{"message":"Could not find a default resolver for travelPassPasswordResetEmail. Please supply a resolver for this mutation.","locations":[{"line":2,"column":3}],"path":["travelPassPasswordResetEmail"]}],"data":null,"status":201,"headers":{"map":{"content-type":"application/json"}}},"request":{"query":"mutation travelPassPasswordResetEmail($email: String) {\n travelPassPasswordResetEmail(email: $email) {\n message\n status\n }\n}","variables":{"email":"test#test.com"}}}
We are using graphql-codegen.
The mutation in the schema:
type Mutation {
travelPassPasswordResetEmail(email: String): SetResetPasswordMutation!
}
The operations graphql file:
mutation travelPassPasswordResetEmail($email: String) {
travelPassPasswordResetEmail(email: $email) {
message
status
}
}
In the types files we get the following generated for the mutation:
export const TravelPassPasswordResetEmail = gql`
mutation travelPassPasswordResetEmail($email: String) {
travelPassPasswordResetEmail(email: $email) {
message
status
}
}
`;
export const TravelPassPasswordResetEmailDocument = gql`
mutation travelPassPasswordResetEmail($email: String) {
travelPassPasswordResetEmail(email: $email) {
message
status
}
}
`;
export function getSdk(client: GraphQLClient, withWrapper: SdkFunctionWrapper = defaultWrapper) {
return {
travelPassPasswordResetEmail(variables?: TravelPassPasswordResetEmailMutationVariables, requestHeaders?: Dom.RequestInit["headers"]): Promise<TravelPassPasswordResetEmailMutation> {
return withWrapper((wrappedRequestHeaders) => client.request<TravelPassPasswordResetEmailMutation>(TravelPassPasswordResetEmailDocument, variables, {...requestHeaders, ...wrappedRequestHeaders}), 'travelPassPasswordResetEmail');
}
};
}
Does anyone know why this is asking for a resolver and it cannot be found? I have tested the graphql on the backend with postman and everything is ok.

I am converting Images to pdf using a npm library in react native why it is giving error of null object?

I am using react-native-image-to-pdf library to convert images to pdf in my react native app. from https://www.npmjs.com/package/react-native-image-to-pdf
var photoPath = ['https://images.pexels.com/photos/20787/pexels-photo.jpg?auto=compress&cs=tinysrgb&h=350','https://images.pexels.com/photos/20787/pexels-photo.jpg?auto=compress&cs=tinysrgb&h=350'];
const myAsyncPDFFunction = async () => {
try {
const options = {
imagePaths: photoPath,
name: 'PDFName',
};
const pdf = await RNImageToPdf.createPDFbyImages(options);
console.log(pdf.filePath);
} catch(e) {
console.log(e);
}
}
but this is giving error Error: Attempt to invoke virtual method 'int android.graphics.Bitmap.getWidth()' on a null object reference
I have also tried giving path as ['./assets/a.png', './assets/b.png']
but still getting same error
Based on the usage example, your photoPath needs to be a local file path and not a remote path.
My recommendation is to first use rn-fetch-blob to download the remote image to the device, and then pass your new local image path to react-native-image-to-pdf. Something like:
RNFetchBlob
.config({
// add this option that makes response data to be stored as a file,
// this is much more performant.
fileCache : true,
})
.fetch('GET', 'http://www.example.com/file/example.png', {
//some headers ..
})
.then(async (res) => {
// the temp file path
console.log('The file saved to ', res.path())
const options = {
imagePaths: [res.path()],
name: 'PDFName',
};
const pdf = await RNImageToPdf.createPDFbyImages(options);
})
from file path remove the text 'file://; with empty string('').
const options = {
imagePaths: [uri.replace('file://', '')],
name: 'FileName',
quality: .9, // optional compression paramter
};
replace('file://', '') it's work for me

Cannot read property 'context' of undefined - GraphQL

I am using Typescript, Express, TypeORM, GraphQL and TypeGraphQL to build a small app that allows the user to login.
However, when I hit my test query bye on the GraphQL playground, I get:
Cannot read property 'context' of undefined
"TypeError: Cannot read property 'context' of undefined",
" at new exports.isAuth // isAuth is a JS file I wrote
MyContext.js
import { Request, Response } from "express";
export interface MyContext {
req: Request;
res: Response;
payload?: { userId: string };
}
isAuth.js
import { MiddlewareFn } from "type-graphql";
import { verify } from "jsonwebtoken";
import { MyContext } from "./MyContext";
export const isAuth: MiddlewareFn<MyContext> = ({ context }, next) => {
const authorization = context.req.headers["authorization"];
if (!authorization) {
throw new Error("not authorized");
}
...
UserResolver
#Query(() => String)
#UseMiddleware(isAuth)
bye(#Ctx() { payload }: MyContext) {
console.log(payload);
return `your user id is: ${payload!.userId}`;
}
I am not sure why the context is undefinied in the file isAuth.js
SOLVED thanks to: https://github.com/MichalLytek/type-graphql/issues/433
1) Go into ./tsconfig.json
2) Change "target": "es5" to "target": "es6"

How do I upload files to a graphql backend implemented using graphql-upload using axios?

I have a GraphQL backend implemented using express, express-graphql, graphql and graphql-upload. My GraphQL schema declaration is as follows:
type OProject {
_id: ID!
title: String!
theme: String!
budget: Float!
documentation: String!
date: Date
}
input IProject {
title: String!
theme: String!
budget: Float!
file: Upload!
}
type Mutations {
create(data: IProject): OProject
}
type Mutation {
Project: Mutations
}
I want to make a create request to my GraphQL API at /graphql using axios. How go I go about it?
Following the GraphQL multipart request specification detailed here you would go about doing so by:
creating a FormData instance and populating it with the following:
The operations field,
the map field and,
the files to upload
Creating the FormData Instance
var formData = new FormData();
The operations field:
The value of this field will be a JSON string containing the GraphQL query and variables. You must set all file field in the variables object to null e.g:
const query = `
mutation($project: IProject!) {
Project { create(data: $project) { _id } }
}
`;
const project = {
title: document.getElementById("project-title").value,
theme: document.getElementById("project-theme").value,
budget: Number(document.getElementById("project-budget").value),
file: null
};
const operations = JSON.stringify({ query, variables: { project } });
formData.append("operations", operations);
The map field:
As its name implies, the value of this field will be a JSON string of an object whose keys are the names of the field in the FormData instance containing the files. The value of each field will be an array containing a string indicating to which field in the variables object the file, corresponding to value's key, will be bound to e.g:
const map = {
"0": ["variables.project.file"]
};
formData.append("map", JSON.stringify(map));
The files to upload
You then should add the files to the FormData instance as per the map. In this case;
const file = document.getElementById("file").files[0];
formData.append("0", file);
And that is it. You are now ready to make the request to your backend using axios and the FormData instance:
axios({
url: "/graphql",
method: "post",
data: formData
})
.then(response => { ... })
.catch(error => { ... });
To complete the answer of #Archy
If you are using definition of what you expect in GraphQl like Inputs don't forget to set your graphql mutation definition after the mutation keyword
You have to put the definition of your payload like this
const query = `
mutation MutationName($payload: Input!) {
DocumentUpload(payload: $payload) {
someDataToGet
}
}`
And your operations like this the operationName and the order doesn't matter
const operations = JSON.stringify({ operationName: "MutationName", variables: { payload: { file: null } }, query })

Angular 2 download PDF from API and Display it in View

I'm learning Angular 2 Beta. I wonder how to download the PDF file from the API and display it in my view? I've tried to make a request using the following:
var headers = new Headers();
headers.append('Accept', 'application/pdf');
var options = new ResponseOptions({
headers: headers
});
var response = new Response(options);
this.http.get(this.setUrl(endpoint), response).map(res => res.arrayBuffer()).subscribe(r=>{
console.log(r);
})
Please note that I only use the console.log to see the value of r
But I always get the following exception message:
"arrayBuffer()" method not implemented on Response superclass
Is it because that method isn't ready yet in Angular 2 Beta? Or is there any mistake that I made?
Any help would be appreciated. Thank you very much.
In fact, this feature isn't implemented yet in the HTTP support.
As a workaround, you need to extend the BrowserXhr class of Angular2 as described below to set the responseType to blob on the underlying xhr object:
import {Injectable} from 'angular2/core';
import {BrowserXhr} from 'angular2/http';
#Injectable()
export class CustomBrowserXhr extends BrowserXhr {
constructor() {}
build(): any {
let xhr = super.build();
xhr.responseType = "blob";
return <any>(xhr);
}
}
Then you need to wrap the response payload into a Blob object and use the FileSaver library to open the download dialog:
downloadFile() {
this.http.get(
'https://mapapi.apispark.net/v1/images/Granizo.pdf').subscribe(
(response) => {
var mediaType = 'application/pdf';
var blob = new Blob([response._body], {type: mediaType});
var filename = 'test.pdf';
saveAs(blob, filename);
});
}
The FileSaver library must be included into your HTML file:
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2014-11-29/FileSaver.min.js"></script>
See this plunkr: http://plnkr.co/edit/tfpS9k2YOO1bMgXBky5Y?p=preview
Unfortunately this will set the responseType for all AJAX requests. To be able to set the value of this property, there are more updates to do in the XHRConnection and Http classes.
As references see these links:
Download pdf file using jquery ajax
Receive zip file, angularJs
Edit
After thinking a bit more, I think that you could leverage hierarchical injectors and configure this provider only at the level of the component that executes the download:
#Component({
selector: 'download',
template: '<div (click)="downloadFile() ">Download</div>'
, providers: [
provide(CustomBrowserXhr,
{ useClass: CustomBrowserXhr }
]
})
export class DownloadComponent {
#Input()
filename:string;
constructor(private http:Http) {
}
downloadFile() {
this.http.get(
'https://mapapi.apispark.net/v1/images/'+this.filename).subscribe(
(response) => {
var mediaType = 'application/pdf';
var blob = new Blob([response._body], {type: mediaType});
var filename = 'test.pdf';
saveAs(blob, filename);
});
}
}
This override would only applies for this component (don't forget to remove the corresponding provide when bootstrapping your application). The download component could be used like that:
#Component({
selector: 'somecomponent',
template: `
<download filename="'Granizo.pdf'"></download>
`
, directives: [ DownloadComponent ]
})
So here is how I managed to get it to work.
My situation: I needed to download a PDF from my API endpoint, and save the result as a PDF in the browser.
To support file-saving in all browsers, I used the FileSaver.js module.
I created a component that takes the ID of the file to download as parameter.
The component, , is called like this:
<pdf-downloader no="24234232"></pdf-downloader>
The component itself uses XHR to fetch/save the file with the number given in the no parameter. This way we can circumvent the fact that the Angular2 http module doesn't yet support binary result types.
And now, without further ado, the component code:
import {Component,Input } from 'angular2/core';
import {BrowserXhr} from 'angular2/http';
// Use Filesaver.js to save binary to file
// https://github.com/eligrey/FileSaver.js/
let fileSaver = require('filesaver.js');
#Component({
selector: 'pdf-downloader',
template: `
<button
class="btn btn-secondary-outline btn-sm "
(click)="download()">
<span class="fa fa-download" *ngIf="!pending"></span>
<span class="fa fa-refresh fa-spin" *ngIf="pending"></span>
</button>
`
})
export class PdfDownloader {
#Input() no: any;
public pending:boolean = false;
constructor() {}
public download() {
// Xhr creates new context so we need to create reference to this
let self = this;
// Status flag used in the template.
this.pending = true;
// Create the Xhr request object
let xhr = new XMLHttpRequest();
let url = `/api/pdf/iticket/${this.no}?lang=en`;
xhr.open('GET', url, true);
xhr.responseType = 'blob';
// Xhr callback when we get a result back
// We are not using arrow function because we need the 'this' context
xhr.onreadystatechange = function() {
// We use setTimeout to trigger change detection in Zones
setTimeout( () => { self.pending = false; }, 0);
// If we get an HTTP status OK (200), save the file using fileSaver
if(xhr.readyState === 4 && xhr.status === 200) {
var blob = new Blob([this.response], {type: 'application/pdf'});
fileSaver.saveAs(blob, 'Report.pdf');
}
};
// Start the Ajax request
xhr.send();
}
}
I've used Font Awesome for the fonts used in the template. I wanted the component to display a download button and a spinner while the pdf is fetched.
Also, notice I could use require to fetch the fileSaver.js module. This is because I'm using WebPack so I can require/import like I want. Your syntax might be different depending of your build tool.
I don't think all of these hacks are necessary. I just did a quick test with the standard http service in angular 2.0, and it worked as expected.
/* generic download mechanism */
public download(url: string, data: Object = null): Observable<Response> {
//if custom headers are required, add them here
let headers = new Headers();
//add search parameters, if any
let params = new URLSearchParams();
if (data) {
for (let key in data) {
params.set(key, data[key]);
}
}
//create an instance of requestOptions
let requestOptions = new RequestOptions({
headers: headers,
search: params
});
//any other requestOptions
requestOptions.method = RequestMethod.Get;
requestOptions.url = url;
requestOptions.responseType = ResponseContentType.Blob;
//create a generic request object with the above requestOptions
let request = new Request(requestOptions);
//get the file
return this.http.request(request)
.catch(err => {
/* handle errors */
});
}
/* downloads a csv report file generated on the server based on search criteria specified. Save using fileSaver.js. */
downloadSomethingSpecifc(searchCriteria: SearchCriteria): void {
download(this.url, searchCriteria)
.subscribe(
response => {
let file = response.blob();
console.log(file.size + " bytes file downloaded. File type: ", file.type);
saveAs(file, 'myCSV_Report.csv');
},
error => { /* handle errors */ }
);
}
Here is the simplest way to download a file from an API that I was able to come up with.
import { Injectable } from '#angular/core';
import { Http, ResponseContentType } from "#angular/http";
import * as FileSaver from 'file-saver';
#Injectable()
export class FileDownloadService {
constructor(private http: Http) { }
downloadFile(api: string, fileName: string) {
this.http.get(api, { responseType: 'blob' })
.subscribe((file: Blob) => {
FileSaver.saveAs(file, fileName);
});
}
}
Call the downloadFile(api,fileName) method from your component class.
To get FileSaver run the following commands in your terminal
npm install file-saver --save
npm install #types/file-saver --save
Hello, here is a working example. It is also suitable for PDF!
application/octet-stream - general type.
Controller:
public FileResult exportExcelTest()
{
var contentType = "application/octet-stream";
HttpContext.Response.ContentType = contentType;
RealisationsReportExcell reportExcell = new RealisationsReportExcell();
byte[] filedata = reportExcell.RunSample1();
FileContentResult result = new FileContentResult(filedata, contentType)
{
FileDownloadName = "report.xlsx"
};
return result;
}
Angular2:
Service xhr:
import { Injectable } from '#angular/core';
import { BrowserXhr } from '#angular/http';
#Injectable()
export class CustomBrowserXhr extends BrowserXhr {
constructor() {
super();
}
public build(): any {
let xhr = super.build();
xhr.responseType = "blob";
return <any>(xhr);
}
}
Install file-saver npm packages "file-saver": "^1.3.3", "#types/file-saver": "0.0.0" and include in vendor.ts import 'file-saver';
Component btn download.
import { Component, OnInit, Input } from "#angular/core";
import { Http, ResponseContentType } from '#angular/http';
import { CustomBrowserXhr } from '../services/customBrowserXhr.service';
import * as FileSaver from 'file-saver';
#Component({
selector: 'download-btn',
template: '<button type="button" (click)="downloadFile()">Download</button>',
providers: [
{ provide: CustomBrowserXhr, useClass: CustomBrowserXhr }
]
})
export class DownloadComponent {
#Input() api: string;
constructor(private http: Http) {
}
public downloadFile() {
return this.http.get(this.api, { responseType: ResponseContentType.Blob })
.subscribe(
(res: any) =>
{
let blob = res.blob();
let filename = 'report.xlsx';
FileSaver.saveAs(blob, filename);
}
);
}
}
Using
<download-btn api="api/realisations/realisationsExcel"></download-btn>
To get Filesaver working in Angular 5: Install
npm install file-saver --save
npm install #types/file-saver --save
In your component use import * as FileSaver from "file-saver";
and use FileSaver.default and not FileSaver.SaveAs
.subscribe(data => {
const blob = data.data;
const filename = "filename.txt";
FileSaver.default(blob, filename);
Here is the code that works for downloadign the API respone in IE and chrome/safari. Here response variable is API response.
Note: http call from client needs to support blob response.
let blob = new Blob([response], {type: 'application/pdf'});
let fileUrl = window.URL.createObjectURL(blob);
if (window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveOrOpenBlob(blob, fileUrl.split(':')[1] + '.pdf');
} else {
window.open(fileUrl);
}
Working solution with C# Web API loading PDF as a byte array:
C# loads PDF as a byte array and converts to Base64 encoded string
public HttpResponseMessage GetPdf(Guid id)
{
byte[] file = GetFile(id);
HttpResponseMessage result = Request.CreateResponse(HttpStatusCode.OK);
result.Content = new StringContent("data:application/pdf;base64," + Convert.ToBase64String(file));
return result;
}
Angular service gets PDF
getPdf(): Observable<string> {
return this.http.get(webApiRequest).pipe(
map(response => {
var anonymous = <any>response;
return anonymous._body;
})
);
}
Component view embeds the PDF via binding to service response
The pdfSource variable below is the returned value from the service.
<embed [src]="sanitizer.bypassSecurityTrustResourceUrl(pdfSource)" type="application/pdf" width="100%" height="300px" />
See the Angular DomSanitizer docs for more info.
http
.post(url, data, {
responseType: "blob",
observe: "response"
})
.pipe(
map(response => {
saveAs(response.body, "fileName.pdf");
})
);
Extending what #ThierryTemplier did (the accepted answer) for Angular 8.
HTML:
<button mat-raised-button color="accent" (click)="downloadFile()">Download</button>
TypeScript:
downloadFile() {
this.http.get(
'http://localhost:4200/assets/other/' + this.fileName, {responseType: 'blob'})
.pipe(tap( // Log the result or error
data => console.log(this.fileName, data),
error => console.log(this.fileName, error)
)).subscribe(results => {
saveAs(results, this.fileName);
});
}
Sources:
FileSaver
Angular Http Client