Logo Oracle Deutschland   Application Express Community
Dateien aus Datenbanktabellen mit APEX bereitstellen: Möglichkeiten ...
Erscheinungsmonat APEX-Version Datenbankversion
Juni 2015 ab 5.0 ab 11.1

Wie man Dateien in eigene APEX-Tabellen hochladen kann, wurde bereits in einem Community-Tipp 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 danach 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 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"-Variante 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 mit der Funktion DBMS_LOB.GETLENGTH zweimal einmal die Dateigröße in Byte ermittelt wird. Eine Spalte wird tatsächlich die Dateigröße anzeigen, die andere wird zum Download-Link werden. Verwenden Sie also die folgende SQL-Abfrage.

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

Das vorläufige Ergebnis sollte wie in Abbildung 2 aussehen ...

Das vorläufige Ergebnis: Ein Bericht

Abbildung 2: Das vorläufige Ergebnis: Ein Bericht

SetrNun 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 im Page Designer zur betreffenden Berichtsspalte und suchen Sie im Property Editor rechts den Bereich Appearance - Format Mask. 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 - der Page Designer wird darauf hin den Bereich BLOB Attributes öffnen, in dem Sie den Download-Link noch weiter konfigurieren können. (Abbildung 4).

"Feinschliff" für den Download-Link

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

Füllen Sie die Felder, wie in Abbildung 4 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. Speichern Sie die Änderungen ab, wenn Sie fertig sind und starten Sie die Seite neu. Sie sollte wie in Abbildung 5 aussehen.

Fertiger Bericht mit funktionierendem Download-Link

Abbildung 5: Fertiger Bericht mit funktionierendem Download-Link

Als nächstes geht es daran, die Spalte zur Anzeige der Dateigröße zu formatieren. Navigieren Sie im Page Designer zum Bericht und dort zur Spalte DATEI_GROESSE. Im Property-Editor suchen Sie wieder die Formatmaske aus. Tragen Sie dort den Text FILESIZE ein (Abbildung 6) und starten Sie die Seite neu..

FILESIZE-Formatmaske für die Spalte mit der Dateigröße einstellen

Abbildung 6: FILESIZE-Formatmaske für die Spalte mit der Dateigröße einstellen

Anstelle der einfachen Anzahl Bytes finden Sie nun - je nach Dateigröße - Angaben in Kilo-, Mega- oder Gigabytes.

FILESIZE-Formatmaske in Aktion

Abbildung 7: FILESIZE-Formatmaske in Aktion"

Und das war es auch schon. Ohne weitere Programmierung können Sie jede Datei aus der Tabelle herunterladen. Allerdings gibt es keine Möglichkeit, den Download irgendwie zu beeinflussen, zusätzlichen Code auszuführen oder das HTML-Markup des Download-Links zu steuern.

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 am besten mit einem AJAX Callback. Öffnen Sie im Strukturbaum links den Reiter fürs Processing und dann das Kontextmenü beim Eintrag AJAX-Callbacks . Klicken Sie dann auf Create Process, um einen neuen AJAX Callback anzulegen.

AJAX Callback im Page Designer erzeugen

Abbildung 8: AJAX Callback im Page Designer erzeugen

Geben Sie dem AJAX Callback einen Namen und hinterlegen Sie den folgenden PL/SQL-Code, wie in Abbildung 9 ersichtlich. Der Code tut eigentlich nichts weiter, als die soeben erzeugte PL/SQL-Prozedur aufzurufen. Zur Übergabe der Datei-ID an die Prozedur verwenden wir kein APEX-Item, sondern einen PL/SQL-Parameter (X01). Dadurch entfällt das ständige Schreiben der übergebenen IDs in den APEX-Session-State.

begin
  #OWNER#.download_blob(
    p_id =>      apex_application.g_x01,
    p_display => false
  );
end;
PL/SQL Code für den AJAX Callback hinterlegen

Abbildung 9: PL/SQL Code für den AJAX Callback hinterlegen

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%20Datei
          &amp;X01=#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%20Datei
          &amp;X01=#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.

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