Retrieving PDF files from a .NET controller using AngularJS

Despite the vast amount of research done on Stack Overflow regarding PDF downloading from a web service, none of the solutions provided have been successful in my case. This is a final attempt to seek help and possibly find answers. The scenario involves making a GET request to an API in order to retrieve a dynamically generated PDF file. Initially, we tried receiving the data as a byte[], but now we are opting for returning a stream containing the content. Here is the snippet from the web service controller:

var result = await resp.Content.ReadAsAsync<byte[]>();
var response = request.CreateResponse(HttpStatusCode.OK);
var dataStream = new MemoryStream(result);
response.Content = new StreamContent(dataStream);
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
response.Content.Headers.ContentDisposition.FileName = "idcard.pdf";

var fileStream = new FileStream(@"c:\temp\temp.pdf", FileMode.Create);
fileStream.Write(result, 0, result.Length);
fileStream.Close();

return response;

The temporary save to the FileStream was just a test to ensure the data can be saved successfully to a file, which worked without any issues. However, saving it silently may not be user-friendly. It would be preferable if the PDF could either open in the browser or prompt for download through the browser.

Reviewing my Angular code reveals the following implementation:

.factory('memberIdCard', ['$http', function($http) {

var get = function() {
    return $http({
        method: 'GET',
        url: '/Member/IdCard',
        headers: {
            accept: 'application/octet-stream'
        },
        responseType: 'arraybuffer',
        transformResponse: function(data) {
            var pdf;
            console.log('data: ', data);
            if (data) {
                pdf = new Blob([data], {
                    type: 'application/pdf'
                });
                console.log('pdf: ', pdf);
            }
            return pdf;
        }
    })
}

return {
    get: get
}
}]);

Attempts using both $http and $resource resulted in failure. Moving on to how this is handled in the controller:

$scope.printIdCard = function() {
memberIdCard.get().then(function(data) {
    var pdf = data.data;
    FileSaver.saveAs(pdf, 'idcard.pdf');

    var pdfUrl = window.URL.createObjectURL(pdf);

    $scope.pdfView = $sce.trustAsResourceUrl(pdfUrl);
    window.open($scope.pdfView);

});

Note: The FileSaver utility is sourced from angular-file-saver.

Following these steps results in a new window opening, but an error message stating "Failed to load PDF Document" appears. Attempts to open the document in Adobe Acrobat lead to another error mentioning compatibility issues or possible file damage due to decoding problems.

Any assistance offered would be highly appreciated. Despite implementing suggestions from various Stack Overflow threads, there might be something vital that I'm overlooking.

Thank you!

Answer №1

I have implemented a similar approach for .xlsx files, adapting the concept to fit my requirements. I found the javascript code to download the file from another source, although I can't recall the exact location.

This is how my web API controller is structured:

[Route("all")]
[HttpGet]
public HttpResponseMessage GetAll(HttpRequestMessage request)
{

HttpResponseMessage response = null;

MemoryStream stream = _exportService.CreateDataStream();

response = request.CreateResponse(HttpStatusCode.OK);

response.Content = new ByteArrayContent(stream.GetBuffer());
response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
response.Content.Headers.Add("content-type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");

return response;
}

Here is the angular service that complements the controller:

(function (app) {

  'use strict';

  app.factory('exportService', exportService);

  exportService.$inject = ['$q', '$http'];

  function exportService($q, $http) {

    var extension = '.xlsx';

    var service = {
        export: exportData
    };

    function exportData(event, fname){

        var config = {
            responseType: 'arraybuffer'
        }

        var path = 'api/export/'+event;

        var deferred = $q.defer();

        return $http.get(path, config).then(
            function(response) {

                var data = response.data;
                var status = response.status;
                var headers = response.headers();

                var octetStreamMime = 'application/octet-stream';
                var success = false;

                var filename = fname + extension;

                var contentType = headers['content-type'] || octetStreamMime;

                try
                {
                    // Try using msSaveBlob if supported
                    var blob = new Blob([data], { type: contentType });
                    if(navigator.msSaveBlob)
                        navigator.msSaveBlob(blob, filename);
                    else {
                        // Try using other saveBlob implementations, if available
                        var saveBlob = navigator.webkitSaveBlob || navigator.mozSaveBlob || navigator.saveBlob;
                        if(saveBlob === undefined) throw "Not supported";
                        saveBlob(blob, filename);
                    }
                    success = true;
                    deferred.resolve();
                } catch(ex)
                {
                }

                if(!success)
                {
                    // Get the blob url creator
                    var urlCreator = window.URL || window.webkitURL || window.mozURL || window.msURL;
                    if(urlCreator)
                    {
                        // Try to use a download link
                        var link = document.createElement('a');
                        if('download' in link)
                        {
                            // Try to simulate a click
                            try
                            {
                                // Prepare a blob URL
                                var blob = new Blob([data], { type: contentType });
                                var url = urlCreator.createObjectURL(blob);
                                link.setAttribute('href', url);

                                // Set the download attribute (Supported in Chrome 14+ / Firefox 20+)
                                link.setAttribute("download", filename);

                                // Simulate clicking the download link
                                var event = document.createEvent('MouseEvents');
                                event.initMouseEvent('click', true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null);
                                link.dispatchEvent(event);
                                success = true;
                                deferred.resolve();
                            } catch(ex) {
                            }
                        }

                        if(!success)
                        {
                            // Fallback to window.location method
                            try
                            {
                                var blob = new Blob([data], { type: octetStreamMime });
                                var url = urlCreator.createObjectURL(blob);
                                window.location = url;
                                success = true;
                                deferred.resolve();
                            } catch(ex) {
                                deferred.reject();
                            }
                        }

                    }
                }
                return deferred.promise;
            },
            function(error) {
                return $q.reject(error);
            });
    }

    return service;
  }

})(angular.module('core.module'));

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What steps are involved in creating a custom cursor for a website?

Is there a way to customize the CSS cursor? CSS cursor customization I'm looking for the code to change the cursor on my website, similar to what is shown in the link below. Check out the Demo Here If possible, could someone provide a brief cod ...

Tips for crafting a precise querySelector

Check out my JavaScript code snippet: navHighlighter() { const sections = document.querySelectorAll("section[id]"); let scrollY = window.pageYOffset; sections.forEach(current => { const sectionHeight = current.clientHeight; const sectio ...

Preventing grid-collection from iterating within a for loop in Liquid on Shopify

I am trying to find a solution to prevent the grid-collection div from iteratig inside a for loop. Below is a snippet of my code in liquid: {% for block in section.blocks %} {% if block.settings.download_title %} <div class="title& ...

What role does NPM play in the deployment of a Node.js App with AWS Beanstalk?

I'm interested in the workflow of an AWS Beanstalk deployment, particularly regarding the installation of packages. I assume that npm is used during the process to install packages on the server(s). However, I am curious to know if AWS Beanstalk utili ...

A guide on accessing $bvToast in Vue class components

I am fairly new to vue.js but I have managed to figure out some things. I initially started with regular js, but then transitioned to typescript with class style vue components. For styling the components, I rely on bootstrap-vue. In my main.ts file, I im ...

Sharing data within angularJS applications can provide numerous benefits and increase

As a beginner in angularJS, I find myself still grappling with the concept of data sharing among various components like controllers, directives, and factories. It seems there are multiple methods available for facilitating communication between them -- ...

Developing a Quarantine Simulation Prototype Using JavaScript

I am in need of a method to determine if any passengers are not healthy. If "isHealthy" is false for any passenger, then the wagon should be quarantined. Additionally, there may be an issue with the join prototype where "isHealthy" is only triggered if a ...

Error encountered while running a Javascript application in Selenium IDE

Occasionally, I encounter this issue while executing test cases in Selenium IDE: An unexpected error occurred. Message: TypeError: testCase.debugContext.currentCommand(...) is undefined Url: chrome://selenium-ide/content/selenium-runner.js, line ...

Unable to fetch the data from HTML input field using autocomplete feature of jQuery UI

I am facing an issue with my html autocomplete textbox. Whenever I try to enter an address like "New York" and click on the submit button, it does not retrieve the input value and returns no value. Test.aspx <!--Address--> <div class="form-g ...

Exploring the world of Django static files, along with the magic of CSS and

Currently running on django 1.3 for production and encountering an issue with using static files in my CSS and JavaScript. Strangely, there are no problems in my HTML code! How can I resolve this dilemma? Unfortunately, the Django documentation does not pr ...

What is the best way to implement smooth scrolling to an anchor tag from a different page in NextJS?

In my NextJS project, I have two main pages - the Home page and the Leaderboard page. The navigation bar code looks like this: <div className={styles.navItem}> <Link href="/"> <a className={styles.navLinks} onClic ...

Connect the endpoint to a duplicate element using jsPlumb

Currently, I am working on a drag and drop application where I am integrating connecting points using jsPlumb. The application features a toolbar of components that users can drag and drop onto the canvas. When a user drops an item, I create a clone and a ...

Enhance your browsing experience with Javascript autocomplete featuring multi-column layouts and organized suggestions

Looking for a JavaScript autocomplete feature that can display categorized suggestions in a multicolumn layout similar to the one found on . Preferably, a jquery or YUI plugin would be ideal. ...

`Automatic toggling between two inputs with adjustable settings`

There are 2 input fields in my code that only accept positive floats with 2 decimals. Any other characters entered should be automatically removed using the change() function. Whenever the value of one input is changed, the value of the other input should ...

Is there a way to identify if a user has made changes to an input box within an input

Is it possible to detect when a field has been changed in a form? The form fields are dynamically generated using AngularJS ng-repeat directive. Here's an example of the formfields object: {"FIELD1":"", "FIELD2":"", "FIELD3":"Prepopulated", "FIELD4": ...

Codeigniter - Ajax request successful in remote server but fails in local server

I am encountering an issue with my web application that makes Ajax requests to a server using Codeigniter-php code. While the Ajax requests work fine on the local server, they do not function properly when the application is hosted on a remote server. The ...

Leveraging dependency injection in Angular 2+ with pre-loaded models

I desire the ability to create an instance of a model using a constructor while also providing injected services to that model. To clarify, I envision something like this: var book = new Book({ id: 5 }); // creates instance, sets id = 5 book.makeHttpCa ...

Platform error: Responses not specified for DIALOGFLOW_CONSOLE

I've been struggling with this issue for almost a day now and I'm at a loss for what else to try. For some reason, Dialogflow Fulfillment in Dialogflow ES is refusing to make any HTTP calls. Every time I attempt, I receive the same error message ...

Empty space on the edge of the mobile screen in Next.js causes usability issues

Currently working on a frontend project using Next.js and Tailwind. Encountered an issue where there is extra space on screens with a width less than 500px. For instance, if the screen width is 400px, there will be an additional 100px of deadspace. Simila ...

Accessing several values in Angular by clicking

I have a dropdown that contains an input field and two more dropdowns. I am looking for a way to retrieve the values of all three fields when the search button is clicked. Screenshot https://i.sstatic.net/5Imaq.png Code HTML <nz-dropdown-menu #menu=&q ...