Andreas Korthaus: (PHP)(HTTP): Excel-Datei wenn möglich im IE ausgeben

Hallo!

Das Thema ist zwar schon älter, aber ich muss gestehen ich schaffe das einfach nicht. Mir ist bewußt dass das dann nur bei bestimmten Clients funktioniert die IE verwenden und Excel entsprechend installiert haben.

Aber es will einfach nicht funktionieren.  Wenn ich direkt eine xls-Datei verlinke, dann wird die wunderbar im Browserfenster geöffnet, wäre zwar auch eine Altermnative, aber nicht für mich da bestimmte Berechtigungen geprüft werden müssen - wobei ich momentan sogar über die Generiereung eines Verzeichnisses mit passender .htaccess die nur den aktuellen Remote-User zulässt, umd die Datei dann dahin zu kopieren.
Also, ich will die xls-Datei über ein PHP-Script ausgeben, nur schaffe ich es nicht mit header() solche HTTP-Header zu erzeugen, dass die Datei wie bei einer direkten Verlinkung  im Browserfenster angezeigt wird.

Was ich aber bekomme ist ein komisches Icon wie bei einem Bild was nicht gefunden wird. Da kann ich dann rechts draufklicken und sagen "nach Excel exportieren", und dann wird es halt in Excel geöffnet.
Ich habe den Body der HTTP-Nachricht verglichen, auf den ersten Blick sieht das 1:1 gleich aus, also bei der original-Datei und bei der generierten Datei.

so generiere ich die Datei

<?php
$excel_file = 'test.xls';
header("Content-Length: ".filesize($excel_file));
header("Content-Type: application/vnd.ms-excel");
readfile($excel_file);
?>

und ja, ich kenne http://www.dclp-faq.de/q/q-code-excel.html, aber der content-disposition Header ändert nichts am Problem, und außerdem wird sowas auch nicht bei einer verlinkten Datei erzeugt.

Hier mal die HTTP-Header:

1. direkter Aufruf der Excel-Datei im IE 6:

GET /tmp/test.xls HTTP/1.1
Host: knet-systems.de
Keep-Alive:
Connection: TE, Keep-Alive
TE: trailers

HTTP/1.1 200 OK
Date: Thu, 05 Jun 2003 07:24:35 GMT
Server: Apache/df-exts 1.1 (Unix) mod_ssl/2.8.11 OpenSSL/0.9.6e AuthPG/1.2 FrontPage/5.0.2.2510
Last-Modified: Wed, 04 Jun 2003 23:38:51 GMT
ETag: "1737df-2000-3ede830b"
Accept-Ranges: bytes
Content-Length: 8192
Keep-Alive: timeout=15, max=100
Connection: Keep-Alive
Content-Type: application/vnd.ms-excel

2. Aufruf des obigen PHP-Scriptes im IE 6:

GET /tmp/header.php HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*
Accept-Language: de
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)
Host: knet-systems.de
Connection: Keep-Alive

HTTP/1.0 200 OK
Date: Thu, 05 Jun 2003 07:23:02 GMT
Server: Apache/df-exts 1.1 (Unix) mod_ssl/2.8.11 OpenSSL/0.9.6e AuthPG/1.2 FrontPage/5.0.2.2510
X-Powered-By: PHP/4.2.3
Content-Length: 8192
Connection: close
Content-Type: application/vnd.ms-excel

Also, wo sind jetzt die Unterschiede? Bei einem Request auf eine xls-Datei erzeugt der IE einen anderen Request-Header, hier fallen vor allem

Connection: TE, Keep-Alive
TE: trailers

auf.

Beim Response kommt dann sowas:

ETag: "1737df-2000-3ede830b"
Accept-Ranges: bytes

Das fehlt bei der selbst generierten Datei. Aber auch wenn ich diese beiden Header manuell in den Response per header() einfüge ändert das nichts am Ergebnis.

Nachzuvollziehen ist das Ganze über:

direkter Link:
http://knet-systems.de/tmp/test.xls
http://forum.de.selfhtml.org/cgi-bin/http_trace.pl?url=http%3A%2F%2Fknet-systems.de%2Ftmp%2Ftest.xls&method=GET&version=HTTP%2F1.1

generierte Datei:
http://knet-systems.de/tmp/header.php
http://forum.de.selfhtml.org/cgi-bin/http_trace.pl?url=http%3A%2F%2Fknet-systems.de%2Ftmp%2Fheader.php&method=GET&version=HTTP%2F1.1

Unterschied ist auch, dass die Antwort des PHP-Scriptes in HTTP/1.0 erfolgt, und nicht 1.1, außerdem wird hier nicht keep-alive verwendet, aber daran wird es wohl kaum liegen, oder? Und nochwas, ich habe es nicht geschafft dass die Datei mit Excel geöffnet wird, im Browser wird immer openoffice verwendet, weiß jemand wo ich das ändern kann? Excel ist bereits standard-Anwendung für .xls, es könnte also auch hieran liegen.

Also ich weiß hier wirklich nicht mehr weiter. Hat denn noch jemand eine Idee was ich falsch mache oder was ich evtl. noch probieren könnte?

Viele Grüße
Andreas

  1. Hallo,

    [ ziemlich viel entfernt *g* ]

    Also ich weiß hier wirklich nicht mehr weiter. Hat denn noch jemand eine Idee was ich falsch mache oder was ich evtl. noch probieren könnte?

    Du machst an sich nichts falsch, nur der IE hat u.a. auch hier einen "Patschen".
    Wenn ich mich reht erinnere, geht der iE nach der Extension des URLs. Wenn Du also http://knet-systems.de/tmp/header.php?wasweisich=x.xls schreibst, dann sollte es auch mit dem IE gehen. Den Content-Type und die Content-Disposion würde ich trotzdem setzen, damit richtige HTTP-Clients  auch richtig bedient werden.

    Grüße
      Klaus

    1. Hallo!

      [ ziemlich viel entfernt *g* ]

      ;-) Das hat mich so einige Stunden und ne Menge Haare gekostet

      Also ich weiß hier wirklich nicht mehr weiter. Hat denn noch jemand eine Idee was ich falsch mache oder was ich evtl. noch probieren könnte?

      Du machst an sich nichts falsch, nur der IE hat u.a. auch hier einen "Patschen".

      Au man...

      Wenn ich mich reht erinnere, geht der iE nach der Extension des URLs.

      Was ist das bitte für ein Quatsch? Kann dich nicht sein dass er nichtmal den Unterschied zw. einer Recource und einem URL-Parameter kennt, und dann noch den Content-Type ignoriert...  gibts doch nicht sowas...

      | Wenn Du also http://knet-systems.de/tmp/header.php?wasweisich=x.xls schreibst, dann sollte es auch mit dem IE gehen.

      Ah, Du hast mich erlöst - vielen, vielen Dank! Ich war kurz davor...

      | Den Content-Type und die Content-Disposion würde ich trotzdem setzen, damit richtige HTTP-Clients  auch richtig bedient werden.
      Wenn das den IE nicht stört... denn hier liegt eindeutig die Priorität. Und wie sicher ist denn die Sache mit dem URL-Parameter? Oder sollte ich lieber das Script in .xls umbenennen und .xls durch den PHP-Interpreter schicken, normales Excel liefere ich eh nicht aus. Oder bekomme ich dann Caching-Probleme, wobei sich das dann mit entsprechenden Headern beheben lassen sollte, ich hoffe wenigstens diese Header beachtet der IE ;-)

      Naja, vielleicht kann ich noch was anderes ausnutzen:
      Im eigentlichen Anwendungsfall  werden sowieso alle Requests an ein PHP-Script geshickt wwelches dann entsprechend dem eigentlichen HTTP-Request ein Scipt läd.
      Ganz grob sowas
      GET /kontakt
      Wird dann umgesetzt zu
      GET / mein_request_script.php?req=kontakt
      in den Script passiert dann irgndwann sowas:
      include(MY_PATH.$_GET['req'].'.php');

      So, jetzt überlege ich, wie ich das mit dem Excel-Script verwenden kann.

      Optimal wäre ja so ein Request:
      GET /write_excel.xls
      dann würde das Script ja write_excel.xls.php
      laden, nur fehlt dann die ID, die entscheidend ist _welche_ excel-Datei erzeugt werden soll, und wenn ich die als Parameter anhänge wird das wohl wieder nicht gehen, heißt also, ich müsste die vor dem .xls unterpringen, was aber auch nicht so das wahre ist, denn so müsste ich das Haupt-Script ändern da ich nicht immer einen anderen Namen für das Excel-Script habe. Schwierig. Ich denke ich mache es wie von Dir vorgeschlagen, aber Du bist Dir sicher das alle aktuellen IEs das können?

      Vielen Dank nochmal und schöne Grüße
      Andreas

      1. Hi,

        Wenn ich mich reht erinnere, geht der iE nach der Extension des URLs.

        Was ist das bitte für ein Quatsch? Kann dich nicht sein dass er nichtmal den Unterschied zw. einer Recource und einem URL-Parameter kennt, und dann noch den Content-Type ignoriert...  gibts doch nicht sowas...

        doch, das gibt es. Es ist unglaublich, dass ein derartiges Programm sich auf dem Markt behaupten kann.

        | Wenn Du also http://knet-systems.de/tmp/header.php?wasweisich=x.xls schreibst, dann sollte es auch mit dem IE gehen.

        Ich würde übrigens sicherheitshalber http://knet-systems.de/tmp/header.xls schreiben und meinen Server entsprechend konfigurieren. Wer weiß, ob der nächste IE etwas "intelligenter"[tm] wird und tatsächlich zwischen Local- und Searchpart unterscheidet ... jiargl ... :-/

        | Den Content-Type und die Content-Disposion würde ich trotzdem setzen, damit richtige HTTP-Clients  auch richtig bedient werden.
        Wenn das den IE nicht stört...

        Nach meiner Erfahrung macht der IE gerade im Hinblick auf die Content-Disposition _ziemlich_ großen Schwachsinn - allerdings abhängig zu dem, was er für die Dateiendung hält (.pl-Ressource mit application/octet-stream und einer Content-Disposition: der IE versucht, den Response in einen zufällig installierten Perl-Interpreter zu jagen). Umso dringlicher der Rat, die URL ohne Searchpart auf .xls enden zu lassen.

        Oder sollte ich lieber das Script in .xls umbenennen und .xls durch den PHP-Interpreter schicken, normales Excel liefere ich eh nicht aus.

        Dennoch würde ich mich bemühen, eine individuellere Konfiguration herzustellen, damit andere .xls-Ressourcen nicht betroffen sind. Beispielsweise könntest Du über mod_rewrite die .xls-URL auf eine .php-Datei schicken.

        Oder bekomme ich dann Caching-Probleme, wobei sich das dann mit entsprechenden Headern beheben lassen sollte, ich hoffe wenigstens diese Header beachtet der IE ;-)

        _Sollte_ gehen :-)

        Optimal wäre ja so ein Request:
        GET /write_excel.xls
        dann würde das Script ja write_excel.xls.php

        Ja, oder write_excel.php. Wenn Du das geschickt anstellst, kannst Du mit einer allgemeinen Konfiguration jede .*-URL (.html, .gif, .bla, ...) über ein PHP-Script schicken, indem Du lediglich ein solches bereitstellst.

        nur fehlt dann die ID, die entscheidend ist _welche_ excel-Datei erzeugt werden soll, und wenn ich die als Parameter anhänge wird das wohl wieder nicht gehen,

        Doch, Du musst diese nur entsprechend beachten.

        Cheatah

        --
        X-Will-Answer-Email: No
        X-Please-Search-Archive-First: Absolutely Yes
        1. Hi!

          Wenn Du also http://knet-systems.de/tmp/header.php?wasweisich=x.xls schreibst, dann sollte es auch mit dem IE gehen.

          Ich würde übrigens sicherheitshalber http://knet-systems.de/tmp/header.xls schreiben und meinen Server entsprechend konfigurieren. Wer weiß, ob der nächste IE etwas "intelligenter"[tm] wird und tatsächlich zwischen Local- und Searchpart unterscheidet ... jiargl ... :-/

          das ist auch meien Befürchtung, glücklicherweise wird die nächste Version aber wohl noch bis zur nächsten Windows-Version auf sich warten lassen, und das dauert AFAIK noch was, und dan dauert es nochwas bis Firmen sowas verwenden, die meisten verwenden ja nichtmal Windows 2000.

          Den Content-Type und die Content-Disposion würde ich trotzdem setzen, damit richtige HTTP-Clients  auch richtig bedient werden.
          Wenn das den IE nicht stört...

          Nach meiner Erfahrung macht der IE gerade im Hinblick auf die Content-Disposition _ziemlich_ großen Schwachsinn - allerdings abhängig zu dem, was er für die Dateiendung hält (.pl-Ressource mit application/octet-stream und einer Content-Disposition: der IE versucht, den Response in einen zufällig installierten Perl-Interpreter zu jagen). Umso dringlicher der Rat, die URL ohne Searchpart auf .xls enden zu lassen.

          Oder sollte ich lieber das Script in .xls umbenennen und .xls durch den PHP-Interpreter schicken, normales Excel liefere ich eh nicht aus.

          Dennoch würde ich mich bemühen, eine individuellere Konfiguration herzustellen, damit andere .xls-Ressourcen nicht betroffen sind. Beispielsweise könntest Du über mod_rewrite die .xls-URL auf eine .php-Datei schicken.

          Ich habe eine Rewrite-Rule die _alle_ Requests(außer Grafiken...) an ein und dassselbe PHP-Script schickt, und ich habe das ganze so wie beschrieben aufgebaut, dass dann automatisch über include das entsprechende PHP-Script geladen wird.

          Diese Struktur muss ich so beibehalten weil die ganze Anwendung drauf basiert, und ich will nach Möglichkeit nicht dieses Script ändern. Und da liegt das problem mit der ID - wo bringe ich die unter?

          dieselbe Tabelle die in Excel vorliegt gebe ich z.B. auch in HTML aus, dann sieht das so aus:

          GET /write_html?id=123

          REWRITE-> GET /mein_script.php?req=write_html&id=123

          in mein_script.php passiert dann das:

          include($_GET['req'].'.php');

          es wird also das Script write_html.php eingebunden.

          Im Script write_html.php steht dann

          $meine_id = $_GET['id'];
          $sql = 'SELECT... WHERE id = $meine_id';

          $db->query($sql);

          ... und dann wird eine HTML-Tabelle erzeugt und ausgeben.

          Bei Excel sol im prinzip dasselbe passieren, mit dem Unterschied dass nicht HTML sondern binäres Excel erzeugt, gespeichert und ausgegeben wird, was ja inzwischen auch funktioniert, nur fehlt mir jetzt nich eine gute Methode die id im Request-String unterzubringen ohne mein_script.php ändern zu müssen, und trotzdem einen Request-String zu haben den der IE halt 'korrekt' interpretiert.

          Mit einem extra Paramater wäre es ja OK:

          GET /write_excel?id=123&file=dummy.xls

          REWRITE-> GET /mein_script.php?req=write_excel&id=123&file=dummy.xls

          in mein_script.php passiert dann wieder das:

          include($_GET['req'].'.php');

          es wird also das Script write_excel.php eingebunden.

          dem Script write_excel.php steht dann wieder folgender Parameter zu Verfügung:

          $my_id = $_GET['id'];

          Und alle sind Glücklich - halt bis auf den Einwand von Dir oben, dass der IE ja auch 'dazulernen' könnte und das ganze schon wieder nicht funktioniert, nur wäre das ja auch kein Schritt nach vorne, wenn schon sollen die gefälligst dem Contend-Type vertrauen und nicht der Endung!!!
          Aber wem sag ich das...  ;-)

          nur fehlt dann die ID, die entscheidend ist _welche_ excel-Datei erzeugt werden soll, und wenn ich die als Parameter anhänge wird das wohl wieder nicht gehen,

          Doch, Du musst diese nur entsprechend beachten.

          Ja, aber wie? Wo bringe ich sie sonst unter? Entweder als Parameter, dann kann es sein dass es in Zukunft evtl. nicht merh geht, oder davor, dann müsste ich meine wichtigsten Scripte ändern, was Auswirkunegn auf jeden einzelnen Request haben wird. Sicher würde das gehen, nur gehört das eigentlich nicht da hin. Da müsste ich dann halt ne Sonderregel schaffen, für den Fall dass die Recource auf .xls endet...

          Ah - ich weiß was ich mache! Getreu dem Motto "doppelt gemoppelt hält besser werde ich so einen Request verwenden:

          GET /write_excel.xls?id=123&file=dummy.xls

          REWRITE-> GET /mein_script.xls.php?req=write_excel&id=123&file=dummy.xls

          in mein_script.php passiert dann wieder das:

          include($_GET['req'].'.php');

          es wird also das Script write_excel.xls.php eingebunden.

          dem Script write_excel.xls.php steht dann auch wieder folgender Parameter zu Verfügung:

          $my_id = $_GET['id'];

          So werde ich es glaube ich machen! Viele Probleme lösen sich beim schreiben... aber ich schicke es trotzdem ab ;-)

          Vielen Dank Euch beiden!

          Grüße
          Andreas

          1. Hi!

            GET /write_excel.xls?id=123&file=dummy.xls

            REWRITE-> GET /mein_script.xls.php?req=write_excel&id=123&file=dummy.xls

            Das ist natürlich quatsch, der obige Request muss heißen:

            GET /mein_script.php?req=write_excel.xls&id=123&file=dummy.xls

            ;-)

            Grüße
            Andreas

          2. Hallo,

            Ja, aber wie? Wo bringe ich sie sonst unter? Entweder als Parameter, dann kann es sein dass es in Zukunft evtl. nicht merh geht,...

            Ich verstehe nicht, wo das Problem mit dem Extraparameter liegen soll. So lange das dahinterliegende Script den Parameter ignoriert, muß doch alles laufen. Und wenn ein zukünftiger IE allen allgemeinen MS-Regeln dann doch noch den Content-Type korrekt unterstützt, dann sollte es auch funktionieren, genauso wie es jetzt schon bei wirklichen HTTP-Useragent korrekt funktioniert, solange eben der Content-Type richtig ist.

            Grüße
              Klaus

            1. Hi!

              Ich verstehe nicht, wo das Problem mit dem Extraparameter liegen soll. So lange das dahinterliegende Script den Parameter ignoriert, muß doch alles laufen. Und wenn ein zukünftiger IE allen allgemeinen MS-Regeln dann doch noch den Content-Type korrekt unterstützt, dann sollte es auch funktionieren, genauso wie es jetzt schon bei wirklichen HTTP-Useragent korrekt funktioniert, solange eben der Content-Type richtig ist.

              Ja, hat sich ja auch erledigt, die Sorge war nur, dass IE anstatt das dann mal richtig zu machen, nur von der total Schwachsinnigen Lösung dne gesamten Request-String als Dateinamen zu betrachten dazu übergeht, nur noch Recourcennamen als solchen zu betrachten und Parameter ignoriert. Daher habe ich den Request jetzt auch so: write_excel.xls?id=123&bla=blib.xls
              reingeschrieben.

              Grüße
              Andreas