Logo Oracle Deutschland   Application Express Community

Einfache "Bildbearbeitung" mit Oracle Application Express

Erscheinungsmonat APEX-Version Datenbankversion
November 2011 ab 4.0 ab 11.2

Der Umgang mit Bilddateien ist für die meisten Web-Anwendungen völlig normal. Zum Thema Dateien in APEX hoch- und wieder herunterladen bzw. darstellen gibt es ja bereits eigene Community-Tipps. Vielfach werden neben diesem einfachen Up- und Download weitergehende Fähigkeiten benötigt ...

  • Größenänderung bzw. Skalierung
  • Umwandeln in Graustufen
  • Drehen, Spiegeln
  • ...

Die Oracle-Datenbank bietet diese und andere Basis-Funktionalitäten zum Bearbeiten von Bildern mit MultiMedia direkt in der Datenbank an. Eine praktische Anwendung ist die automatische Erstellung von Thumbnails für Bilder. In diesem Tipp erfahren Sie, wie Sie diese Funktionen für Ihre eigenen Anwendungen nutzen können.

Erstellen Sie zunächst eine Tabelle namens BILDER_TAB, welche die Bilder aufnehmen soll. Diese sollte aber zwei BLOB-Spalten erhalten - eine nimmt das Originalbild auf, die andere das kleinere Vorschaubild (den Thumbnail). Zusätzlich enthält sie noch Spalten für die Höhe und Breite des Bildes in Pixeln.

create table bilder_tab(
  id        number(10),
  bild      BLOB,
  thumbnail BLOB,
  breite_px number(5), 
  hoehe_px  number(5),
  dateiname varchar2(4000)
)
/

create sequence seq_bilder_tab start with 1 increment by 1
/

Erstellen Sie dann eine Applikation mit einer Seite zum Hochladen von Bildern. Da das verkleinerte Vorschaubild direkt beim Speichern des Bildes generiert werden soll, kommen die APEX-Standardfunktionen nicht in Frage: Es wird eigener PL/SQL-Code benötigt. Insofern erstellen Sie Region, Formularelement (Datei-Upload) und die Schaltfläche zum Absenden am besten einzeln (Abbildungen 1 und 2). Achten Sie darauf, das Element für den Datei-Upload so einzustellen, dass das Bild in WWV_FLOW_FILES und nicht in der Tabelle BILDER_TAB abgelegt wird.

Formularelement zum Datei-Upload erstellen: Ablage in "WWV_FLOW_FILES"

Abbildung 1: Formularelement zum Datei-Upload erstellen: Ablage in WWV_FLOW_FILES

Fertige "Formularseite" zum Bild-Upload

Abbildung 2: Fertige "Formularseite" zum Bild-Upload

Wenn Sie nun eine Datei auswählen und die Schaltfläche Upload klicken, speichert Application Express das Bild automatisch in die Tabelle WWV_FLOW_FILES. Dort steht es als Datentyp BLOB bereit. Sie benötigen daher nun einen PL/SQL-Prozeß, welcher das Bild aus der Tabelle WWV_FLOW_FILES entnimmt und in Ihre eigene Tabelle BILDER_TAB speichert. Im gleichen Atemzug werden wir dann auch den Thumbnail erstellen.

declare
 v_filename  wwv_flow_files.filename%type;

 v_bild      blob := null;
 v_thumb     blob := null;

 v_oi_bild   ordimage := null;
 v_oi_thumb  ordimage := null;

 v_id BILDER_TAB.ID%TYPE;
begin
   begin
     -- Bild aus WWV_FLOW_FILES holen ...
     select blob_content, filename 
      into v_bild, v_filename 
     from wwv_flow_files
     where name = :P1_BILD;

     -- Temporären LOB für Thumbnail erzeugen
     dbms_lob.createtemporary(v_thumb, true, dbms_lob.call);

     -- ORDIMAGE-Objekt für Bild und Thumbnail erzeugen
     v_oi_bild  := ordimage(v_bild);
     v_oi_bild.setproperties();
     v_oi_thumb := ordimage(v_thumb);

     -- Thumbnail generieren
     v_oi_bild.processcopy(
       command => 'maxScale=100 100',
       dest    => v_oi_thumb
     );

     -- Bild und Thumbnail in Tabelle BILDER_TAB ablegen
     insert into bilder_tab (id, bild, thumbnail, dateiname)
     values (seq_bilder_tab.nextval, v_bild, v_oi_thumb.getcontent(), v_filename);

     -- Bild aus WWV_FLOW_FILES entfernen
     delete from wwv_flow_files where name = :P1_BILD;

     -- Temporären LOB freigeben
     dbms_lob.freetemporary(v_thumb);
   exception 
     -- Im Fehlerfall nix machen; in der Praxis aber zumindest Logging!
     when NO_DATA_FOUND then null; 
     when TOO_MANY_ROWS then null; 
   end;
end;

Achten Sie im Code auf das Kommando ORDIMAGE.PROCESSCOPY - damit wird der Thumbnail erstellt und in eine Variable vom Typ BLOB abgelegt. Probieren Sie es nun aus und laden Sie schonmal ein paar Bilder hoch - am besten nehmen Sie größere, hoch aufgelöste Bilder, damit sich die Thumbnail-Berechnung auch lohnt.

Erzeugen Sie als nächstes einen (klassischen) Bericht unterhalb der HTML-Region. Nehmen Sie die folgende SQL-Abfrage als Berichtsquelle.

select 
  id, 
  dateiname, 
  dbms_lob.getlength(thumbnail) vorschau, 
  dbms_lob.getlength(bild)      bildgroesse, 
  breite_px, 
  hoehe_px,
  null as bild_aktion
from bilder_tab

Der Bericht sollte vorerst wie in Abbildung 3 aussehen (Übrigens: Zur Darstellung von Dateigrößen in Byte ist die Formatmaske FILESIZE hervorragend geeignet). Die Spalte BILD_AKTION wird später noch gebraucht.

Bericht auf Tabelle BILDER_TAB

Abbildung 3: Bericht auf Tabelle BILDER_TAB

Man kann schon sehen, dass der Thumbnail mit 1,3KB signifikant kleiner ist als das Originalbild mit ca. 1MB. Der Vorteil von im Voraus berechneten Thumbnails wird sofort deutlich, wenn man sich vorstellt, dass für eine Seite mit bspw. 50 Thumbnails alternativ die Originalbilder zum Browser übertragen und dort kleingerechnet würden. Nun geht es an die Darstellung des Thumbnails selbst. Hierzu können Sie sich der APEX-Standardmittel bedienen. Navigieren Sie zu den Berichtsattributen , dort zur Spalte VORSCHAU und dort zur Formatmaske. Hinterlegen Sie dort folgende Formatanweisung:

IMAGE:BILDER_TAB:THUMBNAIL:ID::::::inline:Herunterladen

Übrigens: Wenn Sie Ihrem Bericht mit DBMS_LOB.GETLENGTH(BILD) noch eine Spalte hinzufügen und diese mit der Formatmaske ...

DOWNLOAD:BILDER_TAB:BILD:ID:::DATEINAME:::inline:Herunterladen

... versehen, haben Sie direkt auch einen Download-Link für das Originalbild. Starten Sie die Seite neu und betrachten Sie das Ergebnis:

Tabelle "BILDER_TAB" mit Thumbnails

Abbildung 4: Tabelle BILDER_TAB mit Thumbnails

Egal wie groß die von Ihnen hochgeladenen Bilder sind, der Bericht wird immer ein Thumbnail mit maximal 100 Pixeln Kantenlänge anzeigen - das sind recht kleine Dateigrößen, so dass der Bericht immer performant erstellt werden kann.

Als nächstes sollen die Spalten für Breite und Höhe des Bildes gefüllt werden; bislang enthalten sie SQL NULL. Der Datentyp ORDIMAGE bietet auch hierfür Funktionen an: GETWIDTH und GETHEIGHT. Gehen Sie also zum vorhin erzeugten PL/SQL-Prozess und tauschen Sie das INSERT-Kommando in BILDER_TAB gegen das folgende aus.

:
insert into bilder_tab (
  id, bild, thumbnail, dateiname, breite_px, hoehe_px
) values (
  seq_bilder_tab.nextval, 
  v_bild, 
  v_oi_thumb.getcontent(), 
  v_filename, 
  v_oi_bild.getwidth(), 
  v_oi_bild.getheight()
);
:

Starten Sie die Seite nochmals neu und laden Sie wiederum ein Bild hoch (Höhe und Breite werden natürlich nur für die ab jetzt hochgeladenen Bilder ermittelt). Sie sehen, dass die Spalten nun gefüllt sind.

Die Berichtsspalten für Höhe und Breite sind nun gefüllt

Abbildung 5: Die Berichtsspalten für Höhe und Breite sind nun gefüllt

Als nächstes geht es daran, noch andere Operationen auf den Bildern durchzuführen - ein interessantes Kommando ist beispielsweise das Umwandeln in Graustufen. Damit man solche Operationen aus dem Bericht heraus starten kann, muss dieser ein wenig geändert werden.

  • Erzeugen Sie eine Tabelle BILD_AKTIONEN und fügen Sie eine Zeile wie folgt ein:
    create table bild_aktionen (
      aktion  varchar2(50) primary key,
      command varchar2(200) not null
    )
    /
    
    insert into bild_aktionen values ('Graustufen','contentFormat=8BITGRAY')
    /
    
  • Ändern Sie das Berichts-SQL wie folgt um. Es wird eine Spalte FORM_ID als HTML HIDDEN-Feld hinzugefügt und die Spalte BILD_OPERATION wird eine Auswahlliste:
    select 
      id,
      apex_item.hidden(1, id) as form_id, 
      dateiname, 
      dbms_lob.getlength(thumbnail) vorschau, 
      dbms_lob.getlength(bild)      bildgroesse, 
      breite_px, 
      hoehe_px,
      apex_item.select_list_from_query(
        2, 
        null, 
        'select aktion d, command r from #OWNER#.bild_aktionen', -- LOV-Abfrage 
        null,                                                    -- zusätzliche HTML-Parameter   
        'YES',                                                   -- NULL anzeigen?   
        'NOOP',                                                  -- NULL Rückgabewert   
        '- Keine Aktion -'                                       -- NULL Anzeigewert   
      ) as  bild_aktion
    from bilder_tab
    
  • Stellen Sie die Anzeige der Spalten FORM_ID und BILD_AKTION in den Spaltenattributen auf Standardberichtsspalte um. Nehmen Sie für FORM_ID auch die Überschrift heraus (Abbildung 7).
    Spalte "FORM_ID" auf Standardberichtsspalte setzen

    Abbildung 6: Spalte FORM_ID auf Standardberichtsspalte setzen

Fügen Sie dann noch eine Schaltfläche zum Durchführen der Aktionen hinzu - nennen Sie diese EXEC_BILD_AKTIONEN und geben Sie ihr das Label Bildaktionen durchführen. Der Bericht sollte dann wie in Abbildung 8 aussehen ...

Im Bericht können nun Bildaktionen gewählt werden

Abbildung 7: Im Bericht können nun Bildaktionen gewählt werden

Nun geht es an den PL/SQL-Prozess, der die konkret gewählten Bildaktionen durchführt. Erzeugen Sie also einen neuen OnSubmit-Prozess vom Typ PL/SQL , nennen Sie ihn Bildaktionen durchführen und hinterlegen Sie folgenden PL/SQL-Code (Achten Sie darauf, bei den Bedingungen einzustellen, dass der Prozess nur bei Klick auf die Schaltfläche EXEC_BILD_AKTIONEN ausgeführt wird).

declare
 v_oi_bild   ordimage := null;
 v_oi_thumb  ordimage := null;

 v_id_array  APEX_APPLICATION_GLOBAL.VC_ARR2 := apex_application.g_f01;
 v_op_array  APEX_APPLICATION_GLOBAL.VC_ARR2 := apex_application.g_f02;
begin
  for i in v_id_array.first..v_id_array.last loop
     if (v_op_array(i) != 'NOOP') then
       begin
         -- Bild aus BILDER_TAB holen und ORDIMAGE-Variable erzeugen
         select ordimage(bild), ordimage(thumbnail)
           into v_oi_bild, v_oi_thumb
         from bilder_tab where id = v_id_array(i) for update nowait; 
    
         -- Bildaktion durchführen
         v_oi_bild.process(v_op_array(i));
         v_oi_bild.setproperties();
    
         -- Thumbnail generieren
         v_oi_bild.processcopy(
           command => 'maxScale=100 100',
           dest    => v_oi_thumb
         );
    
         -- Bild und Thumbnail in Tabelle BILDER_TAB ablegen
         update bilder_tab set
          bild = v_oi_bild.getcontent(),
          thumbnail = v_oi_thumb.getcontent(),
          breite_px = v_oi_bild.getwidth(),
          hoehe_px = v_oi_bild.getheight()
         where id = v_id_array(i);
       exception 
         -- Im Fehlerfall nix machen; in der Praxis aber zumindest Logging!
         when NO_DATA_FOUND then null; 
         when TOO_MANY_ROWS then null; 
       end;
     end if;
  end loop;
  commit;
end;

Probieren Sie es nun aus - stellen Sie die Auswahlliste für eines der Bilder auf Graustufen und klicken Sie die Schaltfläche. Kurze Zeit später sollte genau dieses Bild umgestellt sein (Abbildung 8).

Ein Bild wurde in Graustufen umgewandelt

Abbildung 8: Ein Bild wurde in Graustufen umgewandelt

Das Hinzufügen weiterer Operationen ist nun ganz einfach. Füllen Sie einfach die Tabelle BILD_AKTIONEN entsprechend. Interessant sind unter anderem diese Kommandos ...

  • contentFormat=8BITGRAY: Umwandlung in Graustufen
  • rotate [grad]: Dreht das Bild um [grad] Grad
  • mirror: Spiegelt das Bild
  • gamma [wert größer als 1]: hellt das Bild auf
  • gamma [wert kleiner als 1]: dunkelt das Bild ab

Daraus könnte man folgende Operationen ableiten ...

insert into bild_aktionen values ('Rechts drehen', 'rotate 45');
insert into bild_aktionen values ('Links drehen', 'rotate -45');
insert into bild_aktionen values ('Vertikal spiegeln', 'mirror');
insert into bild_aktionen values ('Horizontal spiegeln', 'flip');
insert into bild_aktionen values ('Aufhellen', 'gamma 2');
insert into bild_aktionen values ('Abdunkeln', 'gamma 0.5');

Und schon stehen weitere Bildoperationen zur Verfügung (Abbildung 9).

Bildoperationen mit Application Express

Abbildung 9: Bildoperationen mit Application Express

Eine besondere Funktion ist das ab Oracle11g Release 2 verfügbare APPLYWATERMARK; damit wird es möglich, ein "Wasserzeichen", also einen Text oder ein anderes Bild, an einer bestimmten Position in das Bild hineinzusetzen - zum Abschluß sei diese Variante erklärt. Zunächst brauchen Sie ein Texteingabefeld für das "Wasserzeichen". Ändern Sie das Berichts-SQL also nochmals wie folgt um:

select 
  id,
  apex_item.hidden(1, id) as form_id, 
  dateiname, 
  dbms_lob.getlength(thumbnail) vorschau, 
  dbms_lob.getlength(bild)      bildgroesse, 
  breite_px, 
  hoehe_px,
  apex_item.text(
    3, 
    null, 
    20,                  -- Größe des Textfeldes 
    20                   -- Maximale Eingabelänge
  ) as wasserzeichen_text,   
  apex_item.select_list_from_query(
    2, 
    null, 
    'select aktion d, command r from #OWNER#.bild_aktionen', 
    null,
    'YES', 
    'NOOP', 
    '- Keine Aktion -'
  ) as  bild_aktion
from bilder_tab

Navigieren Sie auch zu den Spaltenattributen der neuen Spalte WASSERZEICHEN_TEXT und stellen Sie die Anzeige auf Standardberichtsspalte um. Fügen Sie danach eine Operation für die Erstellung von Wasserzeichen in Ihre Tabelle BILD_AKTIONEN ein.

insert into bild_aktionen values ('Wasserzeichen', '-WATERMARK-');

Das Erzeugen eines Wasserzeichens funktioniert technisch jedoch etwas anders als die bisher verwendeten Operationen. Die PL/SQL Funktion ORDIMAGE.PROCESS kommt hier nicht zum Einsatz - vielmehr wird eine eigene Funktion genutzt: APPLYWATERMARK. Also muss der PL/SQL-Prozesscode nochmals erweitert werden ...

declare
 v_oi_bild   ordimage := null;
 v_oi_thumb  ordimage := null;

 v_id_array  APEX_APPLICATION_GLOBAL.VC_ARR2 := apex_application.g_f01;
 v_op_array  APEX_APPLICATION_GLOBAL.VC_ARR2 := apex_application.g_f02;
 v_tx_array  APEX_APPLICATION_GLOBAL.VC_ARR2 := apex_application.g_f03;
begin
  for i in v_id_array.first..v_id_array.last loop
     if (v_op_array(i) != 'NOOP') then
       begin
         -- Bild aus BILDER_TAB holen und ORDIMAGE-Variable erzeugen
         select ordimage(bild), ordimage(thumbnail)
           into v_oi_bild, v_oi_thumb
         from bilder_tab where id = v_id_array(i) for update nowait; 
    
         -- Wasserzeichen ...?
         if v_op_array(i) = '-WATERMARK-' then
          declare
           l_logging varchar2(2000);
           l_prop    ordsys.ord_str_list;
          begin
           l_prop := ordsys.ord_str_list(
             'font_name=Arial',
             'font_style=bold',
             'font_size=50',
             'text_color=green',
             'position_x=10',
             'position_y=90',
             'transparency=0.9'
           );
           v_oi_bild.applyWatermark(
            v_tx_array(i),
            v_oi_bild,
            l_logging,
            l_prop
           );
          end;
         -- oder andere Operation?
         else 
           v_oi_bild.process(v_op_array(i));
           v_oi_bild.setproperties();
         end if;
    
         -- Thumbnail generieren
         v_oi_bild.processcopy(
           command => 'maxScale=100 100',
           dest    => v_oi_thumb
         );
    
         -- Bild und Thumbnail in Tabelle BILDER_TAB ablegen
         update bilder_tab set
          bild = v_oi_bild.getcontent(),
          thumbnail = v_oi_thumb.getcontent(),
          breite_px = v_oi_bild.getwidth(),
          hoehe_px = v_oi_bild.getheight()
         where id = v_id_array(i);
       exception 
         -- Im Fehlerfall nix machen; in der Praxis aber zumindest Logging!
         when NO_DATA_FOUND then null; 
         when TOO_MANY_ROWS then null; 
       end;
     end if;
  end loop;
  commit;
end;

Nun können Sie es ausprobieren ... schaut man sich das Bild anschließend in Originalgröße an, so kann man das Wasserzeichen ganz hervorragend erkennen (Abbildungen 10 und 11).

Bericht mit Möglichkeit zum Setzen eines Wasserzeichens

Abbildung 10: Bericht mit Möglichkeit zum Setzen eines Wasserzeichens

Gesetztes Wasserzeichen

Abbildung 11: Gesetztes Wasserzeichen

Zusammenfassend lässt sich sagen, dass eine ganze Reihe an Bildoperationen mit der Datenbank erledigt werden können - geht es beispielsweise um die Bilder eines Produktkataloges, so können sicherlich alle nötigen Arbeiten mit APEX direkt erledigt werden - der Einsatz zusätzlicher Software dürfte sich in vielen Fällen erübrigen.

Zurück zur Community-Seite