将数组变量中的PDF数据输出到PHP中的输出流


Output PDF data from array variable to output stream in PHP?

我有一个WCF Soap兼容服务,它使用第三方库生成pdf文档。pdf数据以byte[]数组的形式返回,并且从不持久化到磁盘。一个示例界面如下:

[OperationContract]
        int PdfAnswerSheets(
            out byte[] PdfData,
            out int byteLength,
            int quizId,
            string footerLine1,
            string footerLine2,
            string footerLine3,
            string tableHeaderLine,
            string quizTitle,
            string contentLicensedToName,
            string locale,
            bool withCues ,            
            string authId);

该服务运行良好,并按预期返回数据。

接下来,我将使用PHP作为上述服务的客户端。其基本思想是PHP向服务发出请求,然后将PDF数据作为下载发送到客户端浏览器。其目的(非常刻意)是使所有这些工作动态地进行,例如,不必将PDF的副本保存到磁盘上,并通过临时URL等引用它。

这就是PHP的外观

使用WCF服务的函数。这似乎很有效。

// Get answer sheets (web service call)
function wcfAS($quizId, $footerLine1, $footerLine2, $footerLine3, 
        $tableHeaderLine, $quizTitle, $contentLicensedToName,
        $locale, $withCues, $authId)
{
    $client = new SoapClient('http://192.168.241.91:8080/QuizSheetsSvc.svc?wsdl');
    $obj->quizId = $quizId;
    $obj->footerLine1 = $footerLine1;
    $obj->footerLine2 = $footerLine2;
    $obj->footerLine3 = $footerLine3;
    $obj->tableHeaderLine = $tableHeaderLine;
    $obj->quizTitle = $quizTitle;
    $obj->contentLicensedToName = $contentLicensedToName;
    $obj->locale = $locale;
    //$obj->modePreview = false;
    $obj->withCues = $withCues;
    $obj->authId = $authId;
    $retval = $client->PdfAnswerSheets($obj);
    return $retval;
}

触发下载的功能

function AsDownload($quizId, $footerLine1, $footerLine2, $footerLine3, 
        $tableHeaderLine, $quizTitle, $contentLicensedToName,
        $locale, $withCues, $authId)
{
    // Get the response
    $svcResponse = wcfAs($quizId, $footerLine1, $footerLine2, $footerLine3, 
        $tableHeaderLine, $quizTitle, $contentLicensedToName,
        $locale, $withCues, $authId);
    if (!empty($svcResponse->PdfData))
    {
        EchoPdfDownload($svcResponse->PdfData, 'AnswerSheets-'.$quizId.'.pdf', $svcResponse->byteLength);
    }
}

处理浏览器输出的功能

function EchoPdfDownload($data, $fileName, $dataLength)
{
    header('Content-Description: File Transfer');
    header('Content-Type: application/pdf');
    header('Content-Disposition: attachment; filename="'.$fileName.'"');
    header('Content-Transfer-Encoding: binary');
    header('Expires: 0');
    header('Cache-Control: must-revalidate');
    header('Pragma: public');
    header('Content-Length: '.$dataLength);
    ob_clean();
    echo $data;
}

我遇到的问题是最后一部分,即将二进制数据写入输出流,并在浏览器中获得与WCF服务提供的数据匹配的下载。

--使用上面的EchoPdfDownload实现,我在浏览器中得到一个下载文件,该文件的大小明显膨胀(约160%)且已损坏。闻起来像是一路上发生的某种编码或翻译问题--在回显$data之前,我曾尝试修改EchoPdfDownload以在$data上使用PHP的unpack()方法。在这种情况下,我根本无法下载--我尝试过其他各种技术,但收效甚微。

有什么想法吗?

编辑:我试着这样修改下载功能,现在我在浏览器中下载了一个正好1字节长的pdf,而不是膨胀的pdf:

function EchoPdfDownload($data, $fileName, $dataLength)
{
    $binarydata = pack("C*", $data);
    $len = count($binarydata);
    header('Content-Description: File Transfer');
    header('Content-Type: application/pdf');
    header('Content-Disposition: attachment; filename="'.$fileName.'"');
    header('Content-Transfer-Encoding: binary');
    header('Expires: 0');
    header('Cache-Control: must-revalidate');
    header('Pragma: public');
    header('Content-Length: '.$len);
    ob_clean();
    echo $binarydata;
}

EDIT#2:试图通过从服务返回Base64编码的$,在PHP端对其进行解码,然后回声输出数据来解决此问题。这让我回到了最初的问题,即膨胀、损坏的下载。这个问题似乎越来越可能与二进制数据如何作为输出响应的一部分进行传输有关。

function EchoPdfDownload($data, $fileName, $dataLength)
{
    // Decode the data from Base64
    $decoded = base64_decode($data, true);
    header('Content-Description: File Transfer');
    header('Content-Type: application/pdf');
    header('Content-Disposition: attachment; filename="'.$fileName.'"');
    header('Content-Transfer-Encoding: binary');
    header('Expires: 0');
    header('Cache-Control: must-revalidate');
    header('Pragma: public');
    header('Content-Length: '.$len);
    ob_clean();
    echo $decoded;
    ob_end_flush();
}

最后,我应该提到客户端上的AJAX看起来是这样的,以防它相关:

<script>
$(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
        }
}

事实证明,这根本不是PHP问题,而是通过jQuery"ajax"调用接收二进制数据的问题。如果您需要通过ajax调用接收二进制数据,则必须使用二进制传输。

更多详细信息,请访问此链接:http://www.codeproject.com/Questions/879517/Encoding-AJAX-binary-response?arn=1