2012-10-13 8 views
37

Próbuję zbudować plik PDF ze strumienia binarnego, który otrzymuję jako odpowiedź z żądania ajax.Jak zbudować plik PDF z łańcucha binarnego zwrócony z usługi internetowej przy użyciu javascript

Via XmlHttpRequest i otrzymać następujące dane:

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

co starałem tak daleko, żeby osadzić moich danych poprzez dane: Uri.

Teraz nie ma w tym nic złego. Działa dobrze. Niestety nie działa w IE9 i FF.

Możliwym powodem może być to, że FF i IE9 mają problemy z tym użyciem danych-uri.

Teraz szukam rozwiązania, które działa we wszystkich przeglądarkach.

Oto mój kod:

// 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(); 

Jak powiedziałem .... to działa dobrze dla Opery i Safari Chrome (nie testowane). Użycie IE lub FF spowoduje wyświetlenie pustego nowego okna.

Czy istnieje rozwiązanie takie jak tworzenie pliku PDF w systemie plików w celu umożliwienia użytkownikowi pobrania pliku?

Potrzebuję rozwiązania, które działa we wszystkich przeglądarkach przynajmniej w IE, FF, Opera, Chrome i Safari.

Nie mam uprawnień do edytowania implementacji usługi sieciowej. Musiało to być rozwiązanie po stronie klienta.

Jakieś pomysły ???

+1

myślę, że byłoby lepiej dla wszystkich, aby napisać usługę, która pobiera pliki PDF do folder, niech serwer wyświetli pliki statyczne. Na podstawie ścieżki skonstruuj adres URL pliku. –

+0

Uzgodniono z @SrinivasReddyThatiparthy. Pobierz plik tymczasowy i podaj plik użytkownikowi. Możesz go wyświetlić inline, jeśli jest to bezwzględnie wymagane, za pomocą kombinacji tagów object i embed - zobacz to pytanie: http://stackoverflow.com/questions/1244788/embed-vs-object/1244871#1244871 – mccannf

+0

Dlaczego nawet potrzebujesz chwyć dane za pomocą js? Nie możesz po prostu otworzyć nowego okna z location = ? – sander

Odpowiedz

2

Ostatnio widziałem inne pytanie na ten temat (streaming pdf into iframe using dataurl only works in chrome).

Skonstruowałem pliki pdf w ast i przesłałem je do przeglądarki. Tworzyłem je najpierw z fdf, a następnie z klasą pdf, którą sam napisałem - w każdym przypadku plik PDF został utworzony z danych pobranych z obiektu COM na podstawie paru parametrów GET przekazanych za pośrednictwem adresu URL.

Od oglądania danych wysłanych otrzymanych w wywołaniu ajax, wygląda na to, że już prawie jesteś. Od kilku lat nie gram z kodem i nie dokumentowałem go tak dobrze, jak bym sobie tego życzył, ale - myślę, że wszystko, co musisz zrobić, to ustawić cel dla elementu iframe na URL otrzymasz pdf od. Choć może to nie działać - plik, który wychodzi z pliku PDF, może również musieć najpierw wysunąć nagłówek odpowiedzi html.

W skrócie, jest to kod wyjściowy użyłem:

//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; 

Więc nie widząc pełny tekst odpowiedzi fro wywołania AJAX nie mogę być pewien, co to jest, choć jestem skłonni są myśleć, że kod, który wyprowadza żądany plik pdf, może być tylko równoważny z ostatnim wierszem powyższego kodu. Jeśli masz kod, nad którym masz kontrolę, spróbuj ustawić nagłówki - w ten sposób przeglądarka może po prostu zająć się tekstem odpowiedzi - nie musisz nic robić.

Po prostu skonstruowałem adres URL dla potrzebnego pdf (harmonogramu), a następnie utworzyłem ciąg znaków, który reprezentował html dla elementu iframe pożądanego adresu, id itp., Który używał skonstruowanego adresu URL jako src. Jak tylko ustawiłem wewnętrzny html elementu div na skonstruowany ciąg html, przeglądarka poprosiła o plik pdf, a następnie wyświetliła go po odebraniu.

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>"; 
} 

EDYCJA: zapomniałem wspomnieć - możesz wysyłać binarne pliki PDF w ten sposób. Strumienie, które zawierają, nie muszą być kodowane w formacie ASCII85 lub hex. Użyłem flate na wszystkie strumienie w pliku pdf i działało dobrze.

4

zmieniłem to:

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

do

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

i pracował dla mnie.

+0

Działa w przeglądarce Androida? – isobretatel

+1

To nie działa. – Juanjo

+0

@Alexandre, to rozwiązanie nie jest skalowane. – Pacerier

3

Pracuję w PHP i używam funkcji do dekodowania danych binarnych odesłanych z serwera. Wyodrębniam dane wejściowe do prostego pliku file.php i wyświetlam plik za pośrednictwem mojego serwera, a przeglądarka wyświetla artefakt 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); 

?> 
+4

-1. Interesujące, ale nie jest to, o co pytał użytkownik. Poprosił konkretnie od usługi internetowej do Javascript, aby zrobić to po stronie klienta, Nie zakładaj, że użytkownik kontroluje nawet usługę internetową lub używa języka po stronie serwera. – Juanjo

2

Odpowiedź @alexandre z base64 rozwiązuje problem.

Wyjaśnienie, dlaczego to działa na IE jest tutaj

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

Pod nagłówkiem 'format' gdzie jest napisane

Niektóre przeglądarki (Chrome, Opera, Safari, Firefox) zaakceptować non -standardowe zamawianie, jeśli oba, base64 i zestaw znaków są dostarczane, podczas gdy Internet Explorer wymaga, aby specyfikacja zestawu znaków była poprzedzona tokenem base64 .

2

Możesz użyć PDF.js do tworzenia plików PDF z javascript ... kodowanie jest łatwe ... mam nadzieję, że rozwiąże to Twoje wątpliwości !!!

Pozdrawiam!

18

Czy jest jakieś rozwiązanie, takie jak tworzenie pliku PDF w systemie plików w celu , aby użytkownik mógł go pobrać?

spróbuj ustawić responseType z XMLHttpRequest do blob, zastępując atrybut download na a element window.open aby umożliwić pobieranie odpowiedzi z XMLHttpRequest jak .pdf pliku

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(); 
+0

To dzieło doskonale dla mnie. Dzięki – Juanjo

+2

Działa również dla mnie, z wyjątkiem ostatecznego 'removeChild (a)', co zwróciło błąd (coś w stylu "Nie znaleziono węzła"). Tak więc po prostu nie uruchomiłem tego usuwania na 'window.onfocus' i zamiast tego bezpośrednio wstawiłem' document.body.removeChild (a) 'zaraz po' a.click() '. – zezollo

+0

@Zezollo 'window' zyskuje fokus przed zdarzeniem' click'? Prawdopodobnie można go zastąpić przez 'document.body.appendChild (a); a.onclick = function() { window.onfocus = function() { document.body.removeChild (a); window.onfocus = null; } } a.click() ' – guest271314

2

Detect przeglądarkę i wykorzystywać dane-URI dla Chrome i użyj PDF.js jak poniżej w przypadku innych przeglądarek.

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! 
}); 
8

Zdaję sobie sprawę, że jest to raczej stare pytanie, ale tutaj jest rozwiązanie wpadłem dzisiaj:

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); 
} 
+1

Czy potrzebujesz tego z ajax? Możesz po prostu umieścić adres URL jako href i pobrać go bezpośrednio (ale tylko, jeśli jest to proste żądanie pobierania). – Endless

+0

Korzystanie z prostej kotwicy nie jest w naszym przypadku możliwe, AJAX był konieczny. –

Powiązane problemy