How to receive a pdf file via ajax?

Asked

Viewed 3,438 times

4

I am using this method to return a PDF file. It works normal if I call this action directly via URL:

    public ActionResult GerarProva(int idEpo, int numero, bool existeProvaGerada)
    {
        try
        {
            var relatorioBll = new RelatorioBll();
            var dados = relatorioBll.ObterQuestoesProva(numero, idEpo, existeProvaGerada);

            _dadosGeracaoProva = dados;

            System.Web.HttpContext.Current.Response.ContentType = "application/pdf";
            System.Web.HttpContext.Current.Response.AddHeader("content-disposition",
                "attachment;filename=" + ControllerContext.RouteData.Values["action"] + DateTime.Now.ToFileTime() +
                ".pdf");
            System.Web.HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache);

            Stream pdfStream = System.Web.HttpContext.Current.Response.OutputStream;
            GerarPdf<ProvaItem>.GerarProva(pdfStream, dados.ToList());

            System.Web.HttpContext.Current.Response.Write(dados);
            System.Web.HttpContext.Current.Response.End();

            return File(System.Web.HttpContext.Current.Response.OutputStream, "application/pdf");
        }
        catch (Exception ex)
        {
            Danger("Erro: " + ex.Message);
        }

        return RedirectToAction("Index");
    }

But I would like to download the file through ajax. Because I need to perform a javascript action within "Success":

            $.ajax({
                url: link,
                success: function (data) {
                    var blob = new Blob([data]);
                    var link = document.createElement('a');
                    link.href = window.URL.createObjectURL(blob);
                    link.download = "Preview.pdf";
                    link.click();
                },
                error: function (err, er, e) {
                    if (err.responseText.indexOf("encontradas") === -1) {
                        alert("Erro.");
                    } else {
                        alert("Erro: Não foram encontradas questões válidas para a geração da prova.");
                    }
                }
            });

I tried this code, but the pdf comes with the blank pages. An assumption of mine is that my server side method is not returning the correct type...

  • Actually this is a gambiarra. Ajax does not return file. The correct one would be to use a normal link to download.

  • I was using a normal link. However I need to perform a javascript action at the end of the download.

  • So this is not the way. It’s right for you to wait for the post-download action.

  • When I click on the link and the pdf is downloaded I have no way to do the action I wanted via javascript. So I thought of this alternative

  • according to this other gambiarra, you can create a cookie on the client and invalidate it on the server side after the download is completed.

  • more details: http://dejanstojanovic.net/jquery-javascript/2015/march/detect-when-file-download-request-is-finished-with-jquery/

  • Try making the ajax call synchronous. It is asynchronous by default, and this may be causing some problem. I had a similar problem when using an autocomplete plugin (the search method was server-side, called via ajax), and only managed to solve it that way. To make the call synchronous, add the following parameter: async: false,

Show 2 more comments

3 answers

1

The question asks for a solution via ajax, however, the intention is to execute an action at the end of the download so I present a simple solution:

Full example:

    <html>
    <head>
    <script src="js/jquery/jquery-1.11.0.min.js"></script>
    <script type="text/javascript">
    $().ready(function() {

    $('#target').load(function(){
        /*
        Aqui, o download foi completado. (o carregamento do iframe)
        */
        foo();
    });

    $('#file_download').click(function() {
        $("#target").attr("src",$(this).attr('href'));
    });

});

function foo()
{
    console.log('loaded');
}
    </script>
    </head>
    <body>

    <a id="file_download" download="arquivo.pdf" href="http://localhost/arquivo.pdf" > Download PDF </a><br />


    <iframe id="target" src="" width="50" height="10" border="1"></iframe>

    </body>
    </html>

The technique is to download normally, triggered by the element <a>, where the attribute download indicates that the link must be downloaded.

We added a bug for the element click event <a>. The idea is that at the moment of the click, the download will start. In this exact monento, a iframe will be loaded with the same link.

The usefulness of iframe in this technique is due to the method load() which returns the time when the iframe completes the load.

The logic here is, the same file is being uploaded by downloading the element <a> and by iframe.

Both will end dowload at the same time and at that time the desired action is executed.

It can happen that dowload and loading do not complete if the browser shows a dialog box for the user to confirm the execution. At this time, regardless of the user accept or not, iframe will continue loading.

To make the action more discrete, hide the iframe with $('#target').hide().

This may seem like a perfect solution, but the attribute download not supported by IE and Safari.

http://www.w3schools.com/tags/att_a_download.asp

If you want to run in a controlled environment, where the user should use Chrome , Firefox or Opera, this can be a good solution.

Despite this small inconvenience with the attribute download, still it is possible to create implementations for other browsers.

1

Arrange as follows:

Create your AJAX function by receiving as parameter a function to be executed within the Success:

function someAjax(functionToExecute){
    $.ajax({
        type: 'post',
        url: 'www.site.com',
        success: function (response) {
            functionToExecute(response);
        },
        dataType: 'json',
        global: false
    });
}

Function to be executed within Success:

displayFile(file){
    // Faz algo
}

Function that calls the function that encapsulates the AJAX call. Pass as parameter the function name without quotation marks.

function (){
    someAjax(displayFile);
}

0

look, since the expected answer is a blob, then it is interesting to make a request ajax through the Xmlhttprequest and set the responseType to blob.

var uploadFile = document.getElementById("uploadFile");

uploadFile.addEventListener("change", function (event) {
  var link = URL.createObjectURL(event.target.files[0]);
  
  var xmlHttp = new XMLHttpRequest();  
  xmlHttp.open("GET", link, true);
  xmlHttp.responseType = "blob";
  xmlHttp.onreadystatechange = function (e) {
    if (xmlHttp.readyState == 4 && (xmlHttp.status == 200 || xmlHttp.status == 0)) {
      var link = document.createElement('a');
      link.href = URL.createObjectURL(xmlHttp.response);
      link.download = "Preview.pdf";
      link.click();
    }
  }    
  xmlHttp.send();
})
<div>
  <p>será feita uma coisa do arquivo selecionado e o mesmo será baixando por AJAX.</p>
  <input id="uploadFile" type="file" />
</div>

Browser other questions tagged

You are not signed in. Login or sign up in order to post.