Search code examples
angularexpressdocxdocxtemplater

Angular2 downloaded docx response body is empty


I'm using Angular2 to download a generated .docx file from my express server. The file is generated by a tool and saved on the server, and then sent to the frontend.

My browser logging says that the length of the result is larger than 0, but the blob being sent has no body, so the actual contents of the document can't be found.

I've followed several examples and tutorials, especially this one, but no result so far. What am I doing wrong?

express backend:

generate(req: express.Request, res: express.Response): void {
    try {
        var docxGenerator = new DocxGenerator();
        docxGenerator.generate((error, filename) => {
            if (error) res.send(error);
            if (filename) { 
                res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document');
                res.setHeader('Content-disposition', 'attachment; filename=GeneratedFile.docx');
                var file = fs.readFileSync(filename);
                res.end(file);
            }
        });
    } catch (error) {
        res.send(error);
    }
}

Angular2 Service:

export class DocxTemplaterService {

// declarations left out for brevity

constructor(
    private http: Http,
    private authService: AuthenticationService
) {
    this.headers = new Headers({'Content-Type': 'application/json'});
    this.headers.append('Authorization', 'Bearer ' + this.authService.token());
    this.options = new RequestOptions({ headers: this.headers });
}

generateDocx(): Observable<any> {
    this.options.responseType = ResponseContentType.Blob;
    return this.http
        .post(this.docxUtilUrl + '/create', JSON.stringify(module), this.options)
        .map(response => {
            console.dir(response);
            response.blob();
        })
        .catch(this.handleError);
}

private handleError(error: any) {
    return Promise.reject(error.message || error);
}
}

Angular2 Component:

generateDocx(module: Module) {
   this.docxTemplateService
       .generateDocx()
       .subscribe(
            data => {
                console.log(`Word data: ${data}`);
                if (data !== undefined) 
                    // save file somehow
                else console.log('No file to download.');
            },
            error => { this.alertService.error(error); },
            () => this.alertService.success("Success", true)
        );
}

The Blob contents logged in Chrome console:

size: 68192
type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
__proto__: Blob

So... a size without contents. Help is apreciated!


Solution

  • You should explicitly return response.blob from function as you used parenthesis in arrow function.

    generateDocx(): Observable<any> {
        this.options.responseType = ResponseContentType.Blob;
        return this.http
            .post(this.docxUtilUrl + '/create', JSON.stringify(module), this.options)
            .map(response => {
                console.dir(response);
                return response.blob(); //need to return response blob
            })
            .catch(this.handleError);
    }
    

    Or rather you can make that map function one liner

    .map(response => response.blob())
    

    Similar answer here