Logo Oracle Deutschland   Application Express Community
"Dropzone" für Dateien mit Application Express
Erscheinungsmonat APEX-Version Datenbankversion
Januar 2015 alle ab 10.2

Wir bedanken uns bei Franziska Höcker von der MT AG für die Idee, die Umsetzung einer Demoumgebung und wesentlicher Teile dieses Community-Tipps. Eine fertige Umsetzung finden Sie hier (Login: demo / demo).

Drag & Drop Fileupload ist ein Feature, welches in Apex nativ nicht gegeben ist. Mit Hilfe der freien Bibliothek Dropzone.js ist aber leicht möglich, dies zu realisieren. Auch das gleichzeitige Hochladen von mehreren Dateien ist möglich. Das Hochladen der Dateien erfolgt direkt nach dem "Loslassen" der Datei und es ist sichtbar wenn die Datei vollständig hochgeladen wurde. In diesem Community-Tipp wird der komplette Prozess erläutert, welcher von der Einbindung der Javascript-Bibliothek, über die Nutzung im Zusammenspiel mit einem APEX "File-Browse" Element, bis hin zur Verarbeitung der Dateien in der Datenbank reicht. Weiter werden Gestaltungsmöglichkeiten der Dropzone dargestellt, sowie potenziell auftretende Probleme erläutert.

Das Ergebnis: Drag & Drop Upload in einer APEX-Anwendung

Abbildung 1: Das Ergebnis: Drag & Drop Upload in einer APEX-Anwendung

Vorbereitungen

Zunächst wird eine Tabelle benötigt. Neben der Spalte vom Typ BLOB für den Dateiinhalt werden noch weitere für die "Metadaten" der Datei benötigt - am wichtigsten ist eine Spalte für den Dateinamen (DATEINAME) und eine für den Dateityp (MIMETYPE). Legen Sie die Tabelle also wie folgt an.

create table tab_dateien(
  id               number(10),
  dateiname        varchar2(200),
  mimetype         varchar2(100),
  dateiinhalt      blob,
  hochgeladen_am   date not null,
  letzte_aenderung date not null,
  constraint pk_dateien primary key (id)
)
/

create sequence seq_dateien
/

create or replace trigger tr_bui_dateien
before insert or update on tab_dateien
for each row
begin
  if inserting then 
    select seq_dateien.nextval into :new.id from dual; 
    :new.hochgeladen_am := sysdate;
    :new.letzte_aenderung := sysdate;
  end if;
  if updating then
    :new.letzte_aenderung := sysdate;
  end if;
end;
/

Erstellen Sie anschließend eine APEX-Seite, welche die Dropzone aufnehmen soll. Die Seite braucht eine HTML-Region, und in dieser ein normales Element vom Typ Datei hochladen. Nennen Sie es PX_FILES und stellen Sie ein, dass die Dateien in die Tabelle WWV_FLOW_FILES geladen werden sollen (Abbildung 2).

Mit dem File Browse ausgewählte Dateien werden in die Tabelle WWV_FLOW_FILES abgelegt

Abbildung 2: Mit dem File Browse ausgewählte Dateien werden in die Tabelle WWV_FLOW_FILES abgelegt

Legen Sie zum Abschluß noch eine Seitenverzweigung (Page Branch) an. Nach der Seitenverarbeitung soll wieder auf die gleiche Seite zurückverzweigt werden. Wenn Sie nicht die Seite 2 verwenden, passen Sie dies entsprechend an.

Nach dem Upload: Verzweigung auf die gleiche Seite

Abbildung 3: Nach dem Upload: Verzweigung auf die gleiche Seite

Damit haben Sie eine APEX-Seite zum Datei-Upload, die sogar schon ein Stück weit funktioniert. Nun geht es daran, die Seite mit einer Dropzone zum Datei-Upload per Drag & Drop auszustatten.

Einbindung der Bibliothek dropzone.js in APEX

Laden Sie die JavaScript-Bibliothek dropzone.js herunter und dann bei den Gemeinsamen Komponenten als statische Datei in Ihren APEX-Workspace hoch. Vergessen Sie beim Hochladen nicht, die Datei Ihrer Applikation zuzuordnen; sie also als Application File hochzuladen. Navigieren Sie dann zu den Attributen Ihrer APEX-Seite, dort zum Bereich JavaScript und tragen Sie dann bei den File URLs folgendes ein.

#APP_IMAGES#dropzone.js
Dropzone Bibliothek in die APEX-Seite einbinden

Abbildung 4: Dropzone Bibliothek in die APEX-Seite einbinden

Erzeugen Sie dann auf Ihrer Seite eine zweite Region vom Typ HTML Region. Diese bekommt als Quelle einen DIV-Container übergeben. Sehr wichtig dabei ist das HTML-Attribut "id"; denn dieses von der Javascript-Bibliothek referenziert, um die "dropzone" anzuzeigen.

<div id="dropzone" 
     style="border: 1px solid black; width: 500px; height: 400px; overflow: auto;">
  Drop your files here
</div>

Nachdem Sie die Region erstellt haben, befindet sich die "dropzone" bereits auf Ihrer APEX-Seite; allerdings ist sie noch nicht aktiv. Das wird im nächsten Schritt umgesetzt. Platzieren Sie den folgenden Javascript-Code bei den Seitenattributen - im Bereich JavaScript und dort unter Execute when Page loads. Achten Sie darauf, die Variable vItemName in der ersten Zeile auf den Namen Ihres File Browse-Elements zu setzen.

var vItemName = "P2_FILES";

// finde APEX-IDs des Elements "Datei hochladen"
var vArgName = $('#' + vItemName).attr('name');
var vArgNameEnc = $('#' + vItemName).prev('input[type="hidden"][name="p_arg_names"]').val();

Dropzone.autoDiscover = false;

// Initialisiere Dropzone
fDZS = new Dropzone('div#dropzone', {
  "url": "wwv_flow.accept", 
  "params": {
    "p_instance":          $('#pInstance').val(),
    "p_flow_id":           $('#pFlowId').val(),
    "p_flow_step_id":      $('#pFlowStepId').val(),
    "p_page_checksum":     $('#pPageChecksum').val(),
    "p_page_submission_id":$('#pPageSubmissionId').val(),
    "p_request":           ''
  }
});

// Verbinde Dropzone mit dem Element "Datei hochladen"
fDZS.options.paramName = vArgName; 

// für APEX wichtig: Nur eine Datei auf einmal hochladen
fDZS.options.uploadMultiple = false;
fDZS.options.parallelUploads = 1;

fDZS.on('sending',function(file, xhr, formData) {
  formData.append("p_arg_names",vArgNameEnc);
});
Javascript-Code in den Seitenattributen

Abbildung 5: Javascript-Code in den Seitenattributen"

Starten Sie die Seite nun - nun können Sie, aus Ihrem Explorerfenster heraus, mehrere Dateien in die Dropzone ziehen.

Die "dropzone" funktioniert bereits

Abbildung 6: Die "dropzone" funktioniert bereits

Sobald Sie die Dateien in der Dropzone fallenlassen, werden sie bereits hochgeladen und landen in der Tabelle WWV_FLOW_FILES, da Sie diese beim Erstellen des File Browse-Elements als Zieltabelle eingestellt hatten. Im SQL Workshop können Sie das nachvollziehen (Abbildung 7).

select * from wwv_flow_files order by created_on desc
Die Tabelle WWV_FLOW_FILES enthält die per Dropzone hochgeladenen Dateien

Abbildung 7: Die Tabelle WWV_FLOW_FILES enthält die per Dropzone hochgeladenen Dateien

Übernahme der Dateien in die eigene Tabelle

Die Tabelle WWV_FLOW_FILES ist ja nur ein Zwischenschritt; letztendlich sollen die Dateien in die zu Beginn erstellte Tabelle TAB_DATEIEN abgelegt werden. Dazu muss nun noch etwas mehr passieren.

Die Tabelle WWV_FLOW_FILES die Dateien nicht nur unter dem Namen ab, unter dem sie hochgeladen wurde (Spalte FILENAME), sondern zusätzlich unter einem eindeutigen Namen (Spalte NAME) - denn es könnte ja auch sein, dass zwei APEX-Nutzer zwei Dateien des gleichen Names hochladen - dennoch muss man sie auseinanderhalten können. Navigieren Sie also zur HTML-Region, welche das File-Browse Element P2_FILES enthält und tragen Sie dort als Regionsquelle folgendes ein (Achten Sie darauf, den Namen des FileBrowse-Elements an Ihre Umgebung anzupassen).

#####FILEBROWSE####&P2_FILES.#####FILEBROWSE####

Der Grund dafür ist, dass APEX der Dropzone für jede hochgeladene Datei die komplette APEX-Seite als Antwort zurücksendet. Anstelle des Substitution-Strings &P2_FILES. (bzw. Ihr Element-Name) wird die Seite dann aber den eindeutigen Namen der Datei in der Tabelle WWV_FLOW_FILES beinhalten. Diesen kann man dann mit JavaScript bequem ausschneiden und der Dateiliste in P2_FILENAME_LIST hinzufügen.

Fügen Sie nun in den Seitenattributen - wiederum im Bereich Javascript unter Beim Seitenladen ausführen folgenden Javascript-Code hinzu. Dieser Code wird von der Dropzone automatisch ausgeführt, wenn eine Datei erfolgreich zum APEX-Server hochgeladen wurde.

fDZS.on('success',function(a, response) {
  // Variable "response" enthält komplette APEX-Seite
  // eindeutigen Namen der Datei für Tabelle WWV_FLOW_FILES ausschneiden.
  var cut = response.match(/#####FILEBROWSE####.*#####FILEBROWSE####/)[0];
  var sfName = cut.replace(/#####FILEBROWSE####/g, "");
  sfName = $('<div/>').html(sfName).text(); 

  // Dynamic Action aufrufen um Datei in die eigene Tabelle zu übernehemn
  console.log("going to process " + sfName);
  apex.server.process(
    'Dateien speichern',            
    {x01: sfName},
    {
      success: function (pData) { 
        console.log(pData);
      },
      dataType: "text"            
    }
  );
});

Der Code schneidet zunächst den eindeutigen Namen der hochgeladenen Datei aus der Server-Antwort aus und ruft damit einen Application Process namens Datei speichern auf. Dieser soll die Datei von der Tabelle WWV_FLOW_FILES in die eigene Tabelle übertragen und wird jetzt erstellt. Navigieren Sie in der Tree-Ansicht des APEX-Seitenmenüs zu AJAX Callbacks und klicken Sie im Kontextmenü auf Create (Abbildung 8).

Neuen AJAX-Callback erstellen

Abbildung 8: Neuen AJAX-Callback erstellen

Geben Sie dem AJAX-Callback den Namen Datei speichern, wählen Sie als PL/SQL als Typ und und hinterlegen Sie den folgenden PL/SQL Code.

begin
    insert into tab_dateien (DATEINAME, MIMETYPE, DATEIINHALT) (
      select filename, mime_type, blob_content from wwv_flow_files 
      where name = apex_application.g_x01
    );
    delete from wwv_flow_files where name = apex_application.g_x01;
    htp.p(apex_application.g_x01 || ' processed.');
end;

Speichern Sie den Prozess ab und starten Sie die Seite neu. Wenn die Seite noch im Browser ist, machen Sie einen Refresh. Ziehen Sie dann einige Dateien in Ihre Dropzone. Wenn Sie danach Ihre Tabelle TAB_DATEIEN prüfen, sollten Sie bemerken, dass die Dateien nun vorhanden sind.

Styling

Die Dropzone funktioniert also. Allerdings sieht die Version auf der Webseite www.dropzonejs.com doch um einiges besser aus als die unserer APEX-Anwendung. Das liegt daran, dass die zur Dropzone gehörenden CSS- und Bilddateien noch nicht eingebunden sind. Diese können Sie von Github-Seite herunterladen. Sie benötigen.

Die Dateien referenzieren sich gegenseitig mit relativen Pfaden; ein Hochladen in den APEX-Workspace als statische Datei kommt also erst ab APEX Version 5.0 in Betracht. Im aktuellen APEX-Release ist es am besten, diese Dateien auf dem APEX-Webserver bereitzustellen. Legen Sie dazu in dem Verzeichnis, in dem sich die statischen APEX-Dateien befinden (welches über den Webserver-Pfad /i/ erreichbar ist), einen Unterordner dropzone an und legen Sie die drei Dateien darin so ab, dass sie unter folgenden URLs abgerufen werden können.

  • http://{host}:{port}/i/dropzone/css/dropzone.css
  • http://{host}:{port}/i/dropzone/images/spritemap.png
  • http://{host}:{port}/i/dropzone/images/spritemap@2x.png

Navigieren Sie dann nochmals zu den Seitenattributen Ihrer Anwendungsseite und dort in den Bereich CSS. Referenzieren Sie die soeben hochgeladene CSS-Datei dann wie folgt: #IMAGE_PREFIX#dropzone/css/dropzone.css

Dropzone CSS in die APEX-Anwendung einbinden

Abbildung 9: Dropzone CSS in die APEX-Anwendung einbinden

Navigieren Sie dann nochmal zum HTML-Code Ihrer Dropzone-Region. Ändern Sie diesen wie folgt um:

<div id="dropzone" 
     style="border: 1px solid black; width: 500px; height: 400px; overflow: auto;"
     class="dropzone">
  <!-- Drop your files here-->
</div>

Starten Sie die Seite dann nochmals neu - Ihre Dropzone sollte nun besser aussehen.

Aussehen der Dropzone mit korrektem CSS

Abbildung 10: Aussehen der Dropzone mit korrektem CSS

Es stört lediglich noch die obere Region mit dem File Browse-Element und dem "Hilfstext" #####FILEBROWSE####F195073441/filename.txt#####FILEBROWSE####. Diese darf nicht gelöscht werden, denn beides muss vorhanden sein; auch der "Hilfstext" wird benötigt. Es darf auch nicht in den APEX-Bedingungen auf Niemals gestellt werden, denn das bewirkt, dass APEX die Region gar nicht generiert - sie würde auf der Seite fehlen und damit wird die Dropzone nicht funktionieren.

Die Region muss mit CSS-Mitteln ausgeblendet werden. Navigieren Sie zu den Eigenschaften der Region und tragen Sie im Bereich Attribute bei Region Attributes den Text style="display: none;" wie in Abbildung 11 dargestellt ein. Aus Sicht des Servers wird die Region damit stets generiert und an den Browser ausgeliefert (das muss sein). Dieser wird sie allerdings nicht anzeigen.

APEX Region mit CSS-Mitteln ausblenden

Abbildung 11: APEX Region mit CSS-Mitteln ausblenden

Wenn Sie dann noch möchten, navigieren Sie zur HTML-Region der Dropzone und nehmen Sie den Text "Drop your files here" aus dem HTML Quellcode wieder heraus. Das Ergebnis kann sich sehen lassen (Abbildung 12).

Das Ergebnis: Dropzone in einer APEX-Anwendung

Abbildung 12: Das Ergebnis: Dropzone in einer APEX-Anwendung

Die Möglichkeiten der Dropzone-Bibliothek sind damit noch lange nicht ausgereizt; es ist auch möglich, Dateien aus der Dropzone wieder zu entfernen und auf den entsprechenden Javascript-Event zu reagieren (die Datei wieder aus der Tabelle zu löschen). Außerdem ist es möglich, die Anzahl und Größe der Dateien zu begrenzen oder nur bestimmte Dateitypen zuzulassen. Alles in allem lässt sich das also bei Bedarf noch ausbauen. Mehr Informationen enthält die Webseite des Autors www.dropzonejs.com.

Zurück zur Community-Seite