Click here to Skip to main content
15,888,461 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hi,

This is an extension to a question previously posted elsewhere.

I have coded some AJAX that performs a POST to a PHP function. The PHP function returns a PDF document in binary format.

The problem I am experiencing is that the result in the browser is a corrupted Pdf file download that is about 160% larger in file size than it should be. Originally, I suspected this was an issue specific to PHP, but I'm now leaning toward the possibility that it is more to do with how the binary data is processed by the AJAX code, and specifically that the response is delivered using a 8-bit encoding where perhaps a 16-bit encoding is expected/in-use on the client-side. I am trying to look at this problem from all angles rather than just "searching where the light is good".

The relevant PHP functions can be seen here:

http://stackoverflow.com/questions/28662081/output-pdf-data-from-array-variable-to-output-stream-in-php[^]

And this is the AJAX:


C#
$(document).ready(function(){

    $("#dwnAS").click(function(d){
        d.preventDefault();

        var ASreqtype = "AS";
        var ASfooterLine1 = $("#footerText1").val();
        var ASfooterLine2 = $("#footerText2").val();
        var ASfooterLine3 = $("#footerText3").val();
        var AStableHeaderLine  = $("#tableCaption").val();
        var ASquizTitle = $("#quizTitle").val();
        var ASwithCues  = $("#withCues").val();

        if (ASquizTitle=='')
        {
            alert("Please specify a quiz title");
        }
        else
        {
            //disable the submission controls
            $("#dwnAS").attr("disabled", true);
            $("#dwnMS").attr("disabled", true);
            $("#dwnTB").attr("disabled", true);

            var postData =
                'type=' + ASreqtype
                + '&quizId=' + '<?php echo $quizId; ?>'
                + '&footerLine1=' + encodeURIComponent(ASfooterLine1)
                + '&footerLine2=' + encodeURIComponent(ASfooterLine2)
                + '&footerLine3=' + encodeURIComponent(ASfooterLine3)
                + '&tableHeaderLine=' + encodeURIComponent(AStableHeaderLine)
                + '&quizTitle=' + encodeURIComponent(ASquizTitle)
                + '&contentLicensedToName=' + '<?php echo urlencode($contentLicensedToName) ; ?>'
                + '&locale=' + '<?php echo $locale; ?>'
                + '&withCues=' + ASwithCues
                + '&authId=' + '<?php echo urlencode($authId); ?>' ;

            // AJAX Code To Submit Form.
            $.ajax({
                type: "POST",
                url: "subscriber-download-post.php",
                data: postData,
                cache: false,
                success: function(response, status, xhr){

                    $("#dwnAS").attr("disabled", false);
                    $("#dwnMS").attr("disabled", false);
                    $("#dwnTB").attr("disabled", false);

                    downloadresponse(response, status, xhr);



                },
                error: function(response){
                    $("#dwnAS").attr("disabled", false);
                    $("#dwnMS").attr("disabled", false);
                    $("#dwnTB").attr("disabled", false);

                    alert("The download failed. Please try again later.");
                }
            });
        }
    }
)
}) //$(document).ready(function(){

function downloadresponse(response, status, xhr) {
        // check for a filename
        var filename = "";
        var disposition = xhr.getResponseHeader('Content-Disposition');
        if (disposition && disposition.indexOf('attachment') !== -1) {
            var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
            var matches = filenameRegex.exec(disposition);
            if (matches != null && matches[1]) filename = matches[1].replace(/['"]/g, '');
        }

        var type = xhr.getResponseHeader('Content-Type');
        var blob = new Blob([response], { type: type });

        if (typeof window.navigator.msSaveBlob !== 'undefined') {
            // IE workaround for "HTML7007: One or more blob URLs were revoked by closing the blob for which they were created. These URLs will no longer resolve as the data backing the URL has been freed."
            window.navigator.msSaveBlob(blob, filename);
        } else {
            var URL = window.URL || window.webkitURL;
            var downloadUrl = URL.createObjectURL(blob);

            if (filename) {
                // use HTML5 a[download] attribute to specify filename
                var a = document.createElement("a");
                // safari doesn't support this yet
                if (typeof a.download === 'undefined') {
                    window.location = downloadUrl;
                } else {
                    a.href = downloadUrl;
                    a.download = filename;
                    document.body.appendChild(a);
                    a.click();
                }
            } else {
                window.location = downloadUrl;
            }

            setTimeout(function () { URL.revokeObjectURL(downloadUrl); }, 100); // cleanup
        }
}



I have come across this article..
http://stackoverflow.com/questions/8022425/getting-blob-data-from-xhr-request[^]
which touches upon how AJAX handles the binary data response and how it may be possible to deal with it by doing something like ...

HTML
var uInt8Array = new Uint8Array(this.response);


- but I can't quite see how to put it all together. It looks like I may need to completely change this from an "ajax" request to an "xhr" request, if there is no way to handle the encoding issue with adjustments to the existing code?

Assuming that an encoding mismatch is indeed the issue, I'd also be happy with a fix that I can apply on the PHP-side, even if that means doing some extra encoding work at run-time.

If I can't work this out, I'm also considering abandoning my current approach, and re-designing the whole thing to generate temporary documents with a corresponding temporary URL, and then just re-directing the browser per-request. It would just feel like such a shame, as I'm sure it must possible to make this design work, and I've gotten awfully close.

Any advice would be gratefully appreciated.
Posted

It looks like jQuery's ajax method doesn't support binary responses.

Add support for HTML5 XHR v2 with responseType set to 'arraybuffer' on $.ajax[^] - closed as "can't fix".

Ajax: Integrated native datatype, for XHR2 rich responseType support[^] - closed, but with a link to a plugin[^] which claims to add the missing support.

jQuery BinaryTransport[^] - another possible solution, linked from this blog post[^].
 
Share this answer
 
Comments
Sergey Alexandrovich Kryukov 23-Feb-15 15:52pm    
Voted 5. I think you are right, and I think this is due to the scripting nature and origin of AJAX, which is just a part of JavaScript syntax.
At the same time, it's apparent that any binary data can be serialized, but it will produce a chunk of text data of considerably bigger size, so one would pay that traffic cost of it.
—SA
Robert Ellis 23-Feb-15 17:24pm    
The BinaryTransport methodology referenced by Richard works perfectly and is incredibly easy to add to the existing code. So it seems AJAX can be used with binary responses without any real difficulty - you just have to know where to look.
This link Richard Deeming very kindly provided is the correct solution, in the sense that it offers a way to receive and process the binary data correctly. I now get exactly what I want and expect, using the Binary Transport workaround:

https://github.com/henrya/js-jquery/tree/master/BinaryTransport[^]

I will mark Richard's contribution as the correct answer, since he led me to a possible solution.

thanks
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900