Logo Oracle Deutschland   Application Express Community

Einfache "Bildbearbeitung" mit Oracle Application Express

Erscheinungsmonat APEX-Version Datenbankversion
Ausgust 2016 alle 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 APEX_APPLICATION_TEMP_FILES und nicht in der Tabelle BILDER_TAB abgelegt wird.

Element zum Datei-Upload erstellen: Ablage in "APEX_APPLICATION_TEMP_FILES"

Abbildung 1: Element zum Datei-Upload erstellen: Ablage in APEX_APPLICATION_TEMP_FILES

Fertige "Seite" zum Bild-Upload

Abbildung 2: Fertige "Seite" 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 APEX_APPLICATION_TEMP_FILES. Dort steht es als Datentyp BLOB bereit. Sie benötigen daher nun einen PL/SQL-Prozeß, welcher das Bild aus der Tabelle APEX_APPLICATION_TEMP_FILES entnimmt und in Ihre eigene Tabelle BILDER_TAB speichert. Im gleichen Atemzug werden wir dann auch den Thumbnail erstellen.

declare
    v_filename  apex_application_temp_files.filename%type;

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

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

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

    -- Thumbnail generieren
    ordimage.processcopy(
        imageblob => v_bild,
        command   => 'maxScale=100 100',
        dest      => v_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_thumb, v_filename);

    -- Temporären LOB freigeben
    dbms_lob.freetemporary(v_thumb);
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.

declare
    v_filename  apex_application_temp_files.filename%type;

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

    v_oi_bild   ordimage;

    v_id        BILDER_TAB.ID%TYPE;
begin
    -- Bild aus APEX_APPLICATION_TEMP_FILES holen ...
    select blob_content, filename
      into v_bild, v_filename
      from apex_application_temp_files
     where name = :P1_BILD;
    -- Temporären LOB für Thumbnail erzeugen
    dbms_lob.createtemporary(v_thumb, true, dbms_lob.call);
    -- Thumbnail generieren
    ordimage.processcopy(
        imageblob => v_bild,
        command   => 'maxScale=100 100',
        dest      => v_thumb
    );

    -- Bild-Metadaten ermitteln
    v_oi_bild := ordimage(v_bild);
    v_oi_bild.setproperties();

    -- Bild und Thumbnail in Tabelle BILDER_TAB ablegen
    insert into bilder_tab (
        id, bild, thumbnail, dateiname, breite_px, hoehe_px
    ) values (
        seq_bilder_tab.nextval,
        v_bild,
        v_thumb,
        v_filename,
        v_oi_bild.getwidth(),
        v_oi_bild.getheight()
    );
    -- Temporären LOB freigeben
    dbms_lob.freetemporary(v_thumb);
end;

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 das Attribut Escape Special Characters der Spalten FORM_ID und BILD_AKTION in den Spaltenattributen auf No 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 7 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

            -- 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);
        end if;
    end loop;
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)       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

            -- 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=Lucida Sans',
                        'font_size=50',
                        'text_color=red',
                        'position_x=100',
                        'position_y=100',
                        'transparency=1'
                    );
                    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);

        end if;
    end loop;
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.

Wenn Sie mit Ihrer Bildbearbeitung noch einen Schritt weiter gehen möchten, schauen Sie sich den (schon etwas älteren) Community Tipp Bilder manipulieren und bearbeiten: Mit Application Express an. Das dort vorgestellte PL/SQL Paket IMAGE_GENERATOR kann noch wesentlich mehr tun, als Texte in die Bilder zu schreiben.

Zurück zur Community-Seite