Logo Oracle Deutschland   Application Express Community
Dateien aus Datenbanktabellen mit APEX bereitstellen: Möglichkeiten ...

Vor kurzem wurde ein Community-Tipp mit Möglichkeiten zum Upload von Dateien in Tabellen veröffentlicht. Heute beschäftigen wir uns mit der anderen Seite, nämlich den Möglichkeiten, Dateien aus Tabellen zum Download anzubieten oder einfach nur in der Webseite anzuzeigen.

Vorbereitungen

Als Vorbereitung sollen uns die Tabelle und die Beispielanwendung aus besagtem Tipp zum Datei-Upload dienen. Die Anwendungsseite sollte also in etwa wie in Abbildung 1 aussehen.

Ausgangsposition: Anwendungsseite mit "Formular" zum Datei-Upload

Abbildung 1: Ausgangsposition: Anwendungsseite mit "Formular" zum Datei-Upload

Im folgenden erfahren Sie, wie Sie auf der rechten Seite eine Übersicht über die vorhandenen Dateien mit Möglichkeit zum Download einrichten können. Wiederum wird zunächst die einfache out-of-the-box- und danach die manuelle Variante betrachtet.

Datei-Download "out-of-the-box": Ohne Programmierung

Da sich wahrscheinlich viele Dateien in der Tabelle befinden werden, ist zur Darstellung derselben ein Bericht das Mittel der Wahl. Erzeugen Sie also einen klassischen Bericht auf die Tabelle TAB_DATEIEN. Ein interaktiver Bericht ist auch möglich - die folgenden Bildschirmfotos wurden allerdings mit einem klassischen Bericht gemacht. Achten Sie darauf, dass Sie mit der Funktion DBMS_LOB.GETLENGTH wenigstens einmal die Dateigröße in Byte ermitteln - diese Berichtsspalte wird später den Download-Link enthalten. Wenn Sie also die Dateigröße, wie im Beispiel, auch anzeigen möchten, müssen Sie DBMS_LOB.GETLENGTH zweimal verwenden. Verwenden Sie also folgende SQL-Abfrage.

select
  id,
  dateiname,
  mimetype,
  letzte_aenderung,
  dbms_lob.getlength(dateiinhalt) as download_link,
  apex_util.filesize_mask(dbms_lob.getlength(dateiinhalt)) as datei_groesse
from tab_dateien

Das Ergebnis sollte wie in Abbildung 2 aussehen ...

Das vorläufige Ergebnis: Ein Bericht

Abbildung 2: Das vorläufige Ergebnis: Ein Bericht

Nun geht es daran, die Spalte DOWNLOAD_LINK, die im Moment noch die Dateigröße in Byte enthält, in einen Download-Link umzuwandeln. Navigieren Sie hierzu zu den Berichtsattributen und dort zu den Eigenschaften der Spalte DOWNLOAD_LINK. Abbildung 3 zeigt den Bereich der Spaltenattribute, wo unter anderem die Formatmaske festgelegt wird.

Die Formatmaske generiert den Link zum Download einer Datei

Abbildung 3: Die Formatmaske generiert den Link zum Download einer Datei

Tragen Sie, wie in Abbildung 3 ersichtlich, das Schlüsselwort DOWNLOAD, dann den Namen Ihrer Tabelle (TAB_DATEIEN), dann die BLOB-Spalte (DATEIINHALT) und schließlich die Primärschlüsselspalte (ID) ein. Trennen Sie jeweils mit einem Doppelpunkt.

DOWNLOAD:TAB_DATEIEN:DATEIINHALT:ID

Speichern Sie die Änderung ab und klicken Sie in der dann erscheinenden in der Spaltenübersicht wiederum auf den Bleistift der Spalte DOWNLOAD_LINK. Sie landen also im gleichen Dialogfenster (Abbildung 4).

Die Formatmaske generiert den Link zum Download einer Datei: Schritt 2

Abbildung 4: Die Formatmaske generiert den Link zum Download einer Datei: Schritt 2

In Abbildung 4 erkennen Sie, dass nun unterhalb der Formatmaske ein neuer Link BLOB Download Format Mask zu erkennen ist. Klicken Sie darauf - und es öffnet sich ein Fenster, in dem Sie den Download-Link noch weiter bearbeiten können (Abbildung 5).

"Feinschliff" für den Download-Link

Abbildung 5: "Feinschliff" für den Download-Link

Füllen Sie die Felder, wie in Abbildung 5 dargestellt, aus. Geben Sie die Tabellenspalten an, in denen der Mimetype, das Datum der letzten Änderung und der Dateiname steht. Mit der Auswahlliste für die Content Disposition können Sie festlegen, on der Browser die Datei sofort öffnen oder einen Dialog zum Speichern auf die lokale Festplatte anbieten soll. Klicken Sie, wenn Sie fertig sind auf die Schaltfläche Anwenden. Daraufhin setzt APEX anhand Ihrer Angaben die Formatmaske zusammen - sie sollte in etwa so aussehen:

DOWNLOAD:TAB_DATEIEN:DATEIINHALT:ID::MIMETYPE:DATEINAME:LETZTE_AENDERUNG::attachment:Herunterladen

Speichern Sie die Spalte mit Apply Changes bzw. Änderungen anwenden ab; klicken Sie dann in den Berichtsattributen nochmals Apply Changes, um alles zu speichern. Starten Sie die Seite danach erneut - sie sollte wie in Abbildung 6 aussehen.

Fertiger Bericht mit funktionierendem Download-Link

Abbildung 6: Fertiger Bericht mit funktionierendem Download-Link

Und das war es auch schon. Ohne weitere Programmierung können Sie jede Datei aus der Tabelle herunterladen.

Wenn Sie sicher sind, dass Ihre Tabelle nur Bilder enthält, können Sie diese anstelle des Download-Links auch direkt darstellen lassen. Navigieren Sie dazu nochmals zu den Attributen der Spalte DOWNLOAD_LINK und dort zur Formatmaske. Tauschen Sie einfach das erste Schlüsselwort DOWNLOAD durch IMAGE aus und speichern Sie alles ab. Der Bericht sieht danach wie in Abbildung 7 aus.

Das Schlüsselwort IMAGE stellt die Bilder im Bericht direkt dar

Abbildung 7: Das Schlüsselwort IMAGE stellt die Bilder im Bericht direkt dar

Es ist sofort erkennbar, dass nicht alle Bilder problemlos auf diese Weise dargestellt werden können - zu große Bilder können zur Problemen mit dem Seitenlayout führen. Sie sollten sicherstellen, dass Bilder, die Sie auf diese Weise darstellen möchten, die richtige Größe haben.

Mehr Kontrolle: Dateidownload mit PL/SQL

Die bis hierhin beschriebene Vorgehensweise ist sehr einfach; es ist keinerlei Programmierung erforderlich. Manchmal ist jedoch mehr gefordert als das einfache Bereitstellen des BLOBs als Download oder Bild. Bei Download-Links ist es eine naheliegende Anforderung, die Downloads der einzelnen Dateien zu zählen - bei Bildern kann es interessant sein, ein Wasserzeichen einzublenden. Beide Varianten erfordern, dass eigener Code zum Einsatz kommt.

Zunächst geht es um den PL/SQL-Code, der dafür verantwortlich ist, dass ein BLOB als Download oder als Bild im Browser erscheint. Das Grundgerüst ist sehr einfach.

create or replace procedure download_blob (
  p_id       in number,
  p_display  in boolean default false
) is
  v_mimetype   tab_dateien.mimetype%TYPE;
  v_content    tab_dateien.dateiinhalt%TYPE;
  v_name       tab_dateien.dateiname%TYPE;
  v_lastchange tab_dateien.letzte_aenderung%TYPE;
  v_size       number;
begin
  select dateiname, mimetype, dateiinhalt, dbms_lob.getlength(dateiinhalt), letzte_aenderung
    into v_name, v_mimetype, v_content, v_size, v_lastchange 
  from tab_dateien 
  where id = p_id;

  -- Im HTTP-Header wird der Dateityp gesetzt. Damit erkennt der Browser
  -- welche Applikation (bspw. MS Word) zu starten ist
  owa_util.mime_header(nvl(v_mimetype,'application/octet'),false);

  -- Die Dateigröße wird dem Browser ebenfalls mitgeteilt
  htp.p('Content-length:'|| v_size);

  -- Das Datum (LETZTE_AENDERUNG) wird ebenfalls als HTTP-Header gesetzt
  htp.p('Date:'||to_char(v_lastchange,'Dy, DD Mon RRRR hh24:mi:ss')||' CET');

  -- Der Parameter p_display steuert, ob die Datei direkt dargestellt oder
  -- ob dem Anwender ein Download-Dialog angeboten werden soll. Wie man sehen
  -- kann, richtet sich der Browser nach den Feldern im HTTP Header.
  if p_display then 
    htp.p('Content-Disposition: inline');
  else 
    htp.p('Content-Disposition: attachment; filename='||v_name);
  end if;

  -- Der Browser soll die Datei unter keinen Umständen aus dem Cache holen.
  htp.p('Cache-Control: must-revalidate, max-age=0');
  htp.p('Expires: Thu, 01 Jan 1970 01:00:00 CET');

  -- Die Datei bekommt eine ID - damit kann der Browser sie eindeutig erkennen.
  htp.p('Etag: TAB_DATEIEN...'||p_id||'...'||to_char(v_lastchange, 'JHH24MISS'));

  -- Alle HTTP-Header-Felder sind gesetzt
  owa_util.http_header_close;

  -- Dieser kurze Aufruf führt den eigentlichen Datei-Download durch.
  wpg_docload.download_file(v_content);
end;

Spielen Sie den Code in SQL*Plus, dem SQL Developer oder im APEX SQL Workshop ein und erstellen Sie so die Prozedur DOWNLOAD_BLOB . Als nächstes geht es daran, diese Prozedur für APEX verfügbar zu machen - das geschieht mit einem Anwendungsprozess. Navigieren Sie also zu den Gemeinsamen Komponenten und dort zunächst zu den Anwendungelementen. Legen Sie das Element DATEI_ID an und navigieren Sie dann zu den Anwendungsprozessen.

Anwendungselemente und -prozesse bei den "Gemeinsamen Komponenten"

Abbildung 8: Anwendungselemente und -prozesse bei den "Gemeinsamen Komponenten"

Erstellen Sie, wie in Abbildung 9 ersichtlich, einen neuen Anwendungsprozess namens Download Datei. Nehmen Sie einen Bedarfsgesteuerten Prozess.

Ein neuer Bedarfsgesteuerter Prozess wird angelegt

Abbildung 9: Ein neuer Bedarfsgesteuerter Prozess wird angelegt

Hinterlegen Sie dann folgenden PL/SQL-Code als Prozessquelle.

begin
  #OWNER#.download_blob(
    p_id =>      :DATEI_ID,
    p_display => false
  );
end;

Navigieren Sie nun wiederum zu Ihrem Bericht und dort zur Spalte DOWNLOAD_LINK. Entfernen Sie die Formatmaske komplett und navigieren Sie stattdessen zum Bereich HTML-Ausdruck (Abbildung 10). Tragen Sie dort den HTML-Code für den Download-Link ein (übernehmen Sie die Zeilenumbrüche aber nicht; diese dienen allein der besseren Lesbarkeit).

<a href="f?p=&APP_ID.:
             &APP_PAGE_ID.: 
             &SESSION.:
             APPLICATION_PROCESS=Download Datei:::
             DATEI_ID:#ID#" 
   target="_blank">
Datei herunterladen
</a>

Sie merken schon: Bei dieser Variante haben Sie volle Kontrolle nicht nur über den serverseitigen PL/SQL-Code, sondern auch über die Art und Weise, wie der Download-Link zusammengestellt wird.

Download-Link in Berichtsspalte hinterlegen

Abbildung 10: Download-Link in Berichtsspalte hinterlegen

Sie haben nun das gleiche erreicht wie bei der out-of-the-box Variante. (Abbildung 11).

Das Ergebnis: Datei-Download aus APEX heraus

Abbildung 11: Das Ergebnis: Datei-Download aus APEX heraus

Wenn Sie aus dem HTML-Tag <a> ein <img> machen ...

<img src="f?p=&APP_ID.:
              &APP_PAGE_ID.: 
              &SESSION.:
              APPLICATION_PROCESS=Download Datei:::
              DATEI_ID:#ID#" 
     width="300"/>

... werden die Bilder direkt im Bericht dargestellt (Abbildung 12).

Das Ergebnis: Die Bilder sind im Bericht dargestellt

Abbildung 12: Das Ergebnis: Die Bilder sind im Bericht dargestellt

Im zweiten Beispiel wurde die neue Freiheit auch direkt genutzt. Denn während in der out-of-the-box-Variante das Bild stets so groß dargestellt wurde, wie es tatsächlich ist, können Sie die Größe hier mit dem HTML-Attribut width selbst steuern (beachten Sie dabei nur, dass dennoch das ganze Bild vom Browser heruntergeladen wird). Analog dazu sind nun alle möglichen Erweiterungen denkbar. Wenn Sie beispielsweise Datei-Downloads zählen möchten, könnte das wie folgt funktionieren.

  • Erweitern Sie die Tabelle TAB_DATEIEN um eine Spalte DOWNLOAD_ZAEHLER.
    alter table tab_dateien add (download_zaehler number default 0)
    
  • Fügen Sie nun folgenden Code in die Prozedur DOWNLOAD_BLOB ein - ummittelbar hinter der SELECT-Abfrage, welche die Datei aus der Tabelle holt.
    :
      select dateiname, mimetype, dateiinhalt, dbms_lob.getlength(dateiinhalt), letzte_aenderung
        into v_name, v_mimetype, v_content, v_size, v_lastchange 
      from tab_dateien 
      where id = p_id;
    
      update tab_dateien 
        set download_zaehler = download_zaehler + 1 
      where id = p_id;
    
      -- Im HTTP-Header wird der Dateityp gesetzt. Damit erkennt der Browser
      -- welche Applikation (bspw. MS Word) zu starten ist
      owa_util.mime_header(nvl(v_mimetype,'application/octet'),false);
    :
    

Wenn Sie die neue Spalte DOWNLOAD_ZAEHLER dann noch in den Bericht aufnehmen, könnte das Ergebnis wie in Abbildung 13 aussehen.

Bericht mit Downloadzähler

Abbildung 13: Bericht mit Downloadzähler"

Darüber hinaus sind zahlreiche andere Varianten denkbar. Die Datei kann vor der Rückgabe an den Browser noch geändert werden - so wäre es bei Bildern denkbar, ein Wasserzeichen oder einen Copyright-Vermerk anzubringen. Wenn Sie auf einer 11g Release 2-Datenbank arbeiten, gibt es hierfür mit der Prozedur ORDIMAGE.APPLYWATERMARK bereits fertige Funktionalität - für die älteren Datenbankversionen ist im Tipp Bilder manipulieren und bearbeiten: Mit Application Express beschrieben, wie sich Bilder in APEX verändern lassen. Auch das Caching-Verhalten des Browsers lässt sich durch geeignete HTTP-Header-Informationen beeinflussen - hierzu sei auf das hervorragende Blog-Posting von Tyler Muth verwiesen; sie müssen die PL/SQL-Prozedur DOWNLOAD_BLOB nur anhand des Blog-Postings anpassen.

Fazit

Der Umgang mit Bildern und Dateien in APEX ist überhaupt kein Problem. Die bereits vorhandene out-of-the-box Variante bringt schnelle Ergebnisse und dürfte in den meisten Fällen völlig ausreichend sein. Reicht das jedoch nicht aus, so kann man mit eigenem Code auch ausgefallene Anforderungen erfüllen ...

Zurück zur Community-Seite