web-dev-qa-db-fra.com

Comment construire PDF à partir d'une chaîne binaire renvoyée par un service Web à l'aide de javascript

J'essaie de créer un fichier PDF) à partir d'un flux binaire que je reçois en réponse à une demande Ajax.

Via XmlHttpRequest je reçois les données suivantes:

%PDF-1.4....
.....
....hole data representing the file
....
%% EOF

Ce que j’ai essayé jusqu’à présent, c’est d’incorporer mes données via data:uri. Maintenant, il n'y a rien de mal à cela et cela fonctionne bien. Malheureusement, cela ne fonctionne pas dans IE9 et Firefox. Une raison possible peut être que FF et IE9 ont des problèmes avec cet usage du data-uri.

Maintenant, je cherche une solution qui fonctionne pour tous les navigateurs. Voici mon code:

// responseText encoding 
pdfText = $.base64.decode($.trim(pdfText));

// Now pdfText contains %PDF-1.4 ...... data...... %%EOF

var winlogicalname = "detailPDF";
var winparams = 'dependent=yes,locationbar=no,scrollbars=yes,menubar=yes,'+
            'resizable,screenX=50,screenY=50,width=850,height=1050';

var htmlText = '<embed width=100% height=100%'
                     + ' type="application/pdf"'
                     + ' src="data:application/pdf,'
                     + escape(pdfText)
                     + '"></embed>'; 

                // Open PDF in new browser window
                var detailWindow = window.open ("", winlogicalname, winparams);
                detailWindow.document.write(htmlText);
                detailWindow.document.close();

Comme je l’ai dit, cela fonctionne très bien avec Opera et Chrome (Safari n’a pas été testé)). Utilisation de IE ou FF ouvrira une nouvelle fenêtre vierge.

Existe-t-il une solution telle que la création d’un fichier PDF sur un système de fichiers afin de permettre à l’utilisateur de le télécharger?)? J'ai besoin d’une solution qui fonctionne dans tous les navigateurs, du moins dans IE, FF, Opera, etc. Chrome et Safari.

Je n'ai pas la permission de modifier l'implémentation du service Web. Il fallait donc que ce soit une solution côté client. Des idées?

43
Sebb0

Existe-t-il une solution telle que la création d’un fichier pdf sur un système de fichiers afin de laisser l’utilisateur le télécharger?

Essayez de définir responseType sur XMLHttpRequest sur blob, en substituant download attribut à a élément pour window.open pour permettre le téléchargement de la réponse de XMLHttpRequest comme .pdf fichier

var request = new XMLHttpRequest();
request.open("GET", "/path/to/pdf", true); 
request.responseType = "blob";
request.onload = function (e) {
    if (this.status === 200) {
        // `blob` response
        console.log(this.response);
        // create `objectURL` of `this.response` : `.pdf` as `Blob`
        var file = window.URL.createObjectURL(this.response);
        var a = document.createElement("a");
        a.href = file;
        a.download = this.response.name || "detailPDF";
        document.body.appendChild(a);
        a.click();
        // remove `a` following `Save As` dialog, 
        // `window` regains `focus`
        window.onfocus = function () {                     
          document.body.removeChild(a)
        }
    };
};
request.send();
23
guest271314

Je réalise que la question est plutôt ancienne, mais voici la solution que j'ai proposée aujourd'hui:

doSomethingToRequestData().then(function(downloadedFile) {
  // create a download anchor tag
  var downloadLink      = document.createElement('a');
  downloadLink.target   = '_blank';
  downloadLink.download = 'name_to_give_saved_file.pdf';

  // convert downloaded data to a Blob
  var blob = new Blob([downloadedFile.data], { type: 'application/pdf' });

  // create an object URL from the Blob
  var URL = window.URL || window.webkitURL;
  var downloadUrl = URL.createObjectURL(blob);

  // set object URL as the anchor's href
  downloadLink.href = downloadUrl;

  // append the anchor to document body
  document.body.append(downloadLink);

  // fire a click event on the anchor
  downloadLink.click();

  // cleanup: remove element and revoke object URL
  document.body.removeChild(downloadLink);
  URL.revokeObjectURL(downloadUrl);
}
12
Chris Cashwell

J'ai changé ça:

var htmlText = '<embed width=100% height=100%'
                 + ' type="application/pdf"'
                 + ' src="data:application/pdf,'
                 + escape(pdfText)
                 + '"></embed>'; 

à

var htmlText = '<embed width=100% height=100%'
                 + ' type="application/pdf"'
                 + ' src="data:application/pdf;base64,'
                 + escape(pdfText)
                 + '"></embed>'; 

et cela a fonctionné pour moi.

4
Alexandre Almeida

Je travaille dans PHP et utilise une fonction pour décoder les données binaires renvoyées par le serveur. J'extrais les informations d'une entrée dans un fichier file.php et je vis le fichier via mon serveur et tous les navigateurs. afficher l'artefact pdf.

<?php
   $data = 'dfjhdfjhdfjhdfjhjhdfjhdfjhdfjhdfdfjhdf==blah...blah...blah..'

   $data = base64_decode($data);
    header("Content-type: application/pdf");
    header("Content-Length:" . strlen($data ));
    header("Content-Disposition: inline; filename=label.pdf");
    print $data;
    exit(1);

?>
3
Fabiolus

Détectez le navigateur et utilisez Data-URI pour Chrome et utilisez PDF.js comme ci-dessous pour les autres navigateurs.

PDFJS.getDocument(url_of_pdf)
.then(function(pdf) {
    return pdf.getPage(1);
})
.then(function(page) {
    // get a viewport
    var scale = 1.5;
    var viewport = page.getViewport(scale);
    // get or create a canvas
    var canvas = ...;
    canvas.width = viewport.width;
    canvas.height = viewport.height;

    // render a page
    page.render({
        canvasContext: canvas.getContext('2d'),
        viewport: viewport
    });
})
.catch(function(err) {
    // deal with errors here!
});
2
Ghostoy

J'ai récemment vu une autre question sur ce sujet ( le streaming de fichiers PDF dans iframe à l'aide de dataurl ne fonctionne qu'en chrome ).

J'ai construit des fichiers PDF au format ast et les ai transmis au navigateur. Je les créais d'abord avec fdf, puis avec une classe de pdf que j'ai moi-même écrite. Dans chaque cas, le pdf était créé à partir de données extraites d'un objet COM basé sur quelques paramètres GET transmis via l'URL.

En regardant vos données envoyées reçues dans l'appel ajax, il semble que vous y soyez presque. Je n'ai pas joué avec le code depuis deux ans et je ne l'ai pas documenté aussi bien que je l'aurais voulu, mais - je pense que tout ce que vous avez à faire est de définir l'objectif d'un iframe comme étant l'URL. vous obtenez le pdf de. Bien que cela puisse ne pas fonctionner - le fichier qui remplace le pdf peut également devoir supprimer un en-tête de réponse HTML en premier.

En un mot, voici le code de sortie que j'ai utilisé:

//We send to a browser
header('Content-Type: application/pdf');
if(headers_sent())
    $this->Error('Some data has already been output, can\'t send PDF file');
header('Content-Length: '.strlen($this->buffer));
header('Content-Disposition: inline; filename="'.$name.'"');
header('Cache-Control: private, max-age=0, must-revalidate');
header('Pragma: public');
ini_set('zlib.output_compression','0');
echo $this->buffer;

Donc, sans voir le texte complet de la réponse de l'appel ajax, je ne peux pas vraiment savoir de quoi il s'agit, bien que je sois enclin à penser que le code qui produit le pdf que vous demandez n'est peut-être que l'équivalent du dernier. ligne dans le code ci-dessus. Si c'est le code que vous contrôlez, j'essaierai de définir les en-têtes - de cette façon, le navigateur ne pourra traiter que le texte de la réponse - vous n'avez pas à vous soucier de quoi que ce soit.

J'ai simplement construit une URL pour le pdf que je voulais (un calendrier), puis j'ai créé une chaîne de caractères représentant le code html d'un iframe du fichier souhaité, id, etc., qui utilisait l'URL construite telle que src. Dès que j'ai défini le code HTML interne d'un div sur la chaîne HTML construite, le navigateur a demandé le fichier PDF, puis l'a affiché lors de sa réception.

function  showPdfTt(studentId)
{
    var url, tgt;
    title = byId("popupTitle");
    title.innerHTML = "Timetable for " + studentId;
    tgt = byId("popupContent");
    url = "pdftimetable.php?";
    url += "id="+studentId;
    url += "&type=Student";
    tgt.innerHTML = "<iframe onload=\"centerElem(byId('box'))\" src='"+url+"' width=\"700px\" height=\"500px\"></iframe>";
}

EDIT: oublié de mentionner - vous pouvez envoyer des pdf binaires de cette manière. Les flux qu’ils contiennent n’ont pas besoin d’être encodés en ascii85 ou en hexadécimal. J'ai utilisé flate sur tous les flux du pdf et tout a bien fonctionné.

2
enhzflep

La réponse de @alexandre avec base64 fait l'affaire.

La raison pour laquelle cela fonctionne pour IE est ici

https://en.m.wikipedia.org/wiki/Data_URI_scheme

Sous en-tête 'format' où il est écrit

Certains navigateurs (Chrome, Opera, Safari, Firefox) acceptent un ordre non standard si les deux types de caractères sont fournis: base64 et; charset, tandis qu'Internet Explorer exige que la spécification du jeu de caractères précède le jeton base64.

2
Hace

Vous pouvez utiliser PDF.js pour créer PDF à partir de javascript ... il est facile de coder ... espérons que cela résoudra vos doutes !!!

Cordialement!

1
Fernando Canul