Logo Oracle Deutschland   Application Express Community
Dateitransfer in den Verzeichnisbaum des Betriebssystems
von Heinz-Josel Wrobel, DATA MART Consulting GmbH (http://www.datamart.de/)

Erscheinungsmonat APEX-Version Datenbankversion
Juni 2011 ab 3.x ab 9.2

In den vergangenen Newslettern wurde mehrmals über das Dateihandling berichtet. In diesem Beitrag möchte ich einen Weg vorstellen, Dateien über APEX in das Verzeichnis des Betriebssystems zu platzieren. Der vorliegende Weg ermöglicht das Dateihandling selbst auf einer XE-Datenbank. Der Tipp basiert auf dem bereits vorhandenen Tipp Dateien mit APEX in Datenbanktabellen hochladen: Möglichkeiten..

Dies erläutere ich an dieser Stelle nicht erneut, sondern verweise nur auf die entsprechenden Seiten im Tutorial. Nachdem nun die gewünschte Datei mit dem File-Browse-Button bestimmt wurde und die Seite "abgeschickt" bzw. "submitted" wurde, liegt diese Datei als BLOB-File in der Datenbank im Schema FLOWS_FILES in der Tabelle WWV_FLOW_FILE_OBJECTS$. Diese wird jedoch nicht direkt angesprochen - der Zugriff erfolgt über die View WWV_FLOW_FILES. In besagtem Tutorial wird die Datei von dort aus in die Zieltabelle kopiert - hier werden wie Sie in das Verzeichnis des Betriebssystem platzieren.

Die PL/SQL-Prozedur hierfür benötigt zwei Übergabeparameter, zum einen den Dateinamen der zu transportierenden Datei und zum anderen den Verzeichnisnamen, also den Ort, an dem die Datei platziert werden soll. Der Verzeichnisname muss zuvor vom DBA als Directory in der Datenbank definiert werden und das APEX-Parsing-Schema muss mit den nötigen Schreib- und Leserechten ausgestattet werden.

create directory apex_application_file_dir as '/opt/meine-dateien/apex/application-100'
/

grant read, write on directory apex_application_file_dir to {apex-parsing-schema}
/

Der Name der Datei kann aus einem APEX-Element (bspw. das File-Browse-Item) entmommen werden. Alternativ könnten über einen Report alle Dateien aus der View WWV_FLOW_FILES angezeigt werden und so eine spezielle Datei bearbeitet werden. Der zweite Parameter, der Verzeichnisname, ist optional. Er ist konstant und könnte auch innerhalb der Prozedur definiert sein. Andererseits könnte es auch erforderlich sein, unterschiedliche Dateien in unterschiedliche Verzeichnisse abzulegen. So erhöht der Verzeichnis-Parameter die Flexibilität beim Arbeiten mit dieser Prozedur.

  • Zunächst werden die Dateiinhalte mitsamt der Metadaten wie Dateiname oder Dateigröße aus der View WWV_FLOW_FILES entnommen.
  • Unter Zuhilfenahme des Datenbank-Packages UTL_FILE wird das Blob-File nun ins Dateisystem geschrieben. Dateien bis zu einer Größe von 32kB können direkt, größere Dateien müssen in Chunks zerlegt und schrittweise geschrieben werden, da UTL_FILE nicht direkt mit BLOBs umgehen kann.
  • Falls dieselbe Datei mehrmals hochgeladen wurde, lässt sich die Datei dennoch eindeutig über die Spalte NAME bestimmen. Beim Upload wird der Dateiname mit einem Präfix eindeutig gemacht. Der Inhalt des File-Browse-Items wird entsprechend angepasst.
  • Wenn die Datei ins Dateisystem geschrieben wurde, kann sie bei Bedarf mit einem SQL DELETE Kommando wieder aus WWV_FLOW_FILES entfernt werden.
PROCEDURE blob2file(p_filename VARCHAR2, p_dirname IN VARCHAR2 default 'APEX_APPLICATION_FILE_DIR') IS
 vblob   BLOB;
 vstart  NUMBER := 1;
 bytelen NUMBER := 32000;
 len     NUMBER;
 my_vr   RAW(32000);
 x       NUMBER;
 l_output utl_file.file_type;
 vc_filename wwv_flow_files.filename%TYPE;
 vc_texttype wwv_flow_files.mime_type%TYPE;
 vc_errm VARCHAR2(4000);
BEGIN
  -- get length of blob
  -- select blob into variable
  select filename, dbms_lob.getlength(blob_content), blob_content, mime_type
    into vc_filename, len, vblob, vc_texttype
    from wwv_flow_files 
   where rownum = 1
     and name = p_filename;

  -- define output directory
  vc_filename := 'Dateiname_ist'||vc_filename;
  l_output := utl_file.fopen(p_dirname, vc_filename, 'WB', 32760);  -- save blob length
  x := len;

  IF len <= 0 then null;
  -- if small enough for a single write
  ELSIF len < 32760 THEN
    utl_file.put_raw(l_output,vblob);
    utl_file.fflush(l_output);
  ELSE -- write in pieces
    vstart := 1;
    WHILE vstart < len
    LOOP
      dbms_lob.read(vblob,bytelen,vstart,my_vr);

      utl_file.put_raw(l_output,my_vr);
      utl_file.fflush(l_output);

      -- set the start position for the next cut
      vstart := vstart + bytelen;

      -- set the end position if less than 32000 bytes
      x := x - bytelen;
      IF x < 32000 THEN
        bytelen := x;
      END IF;
    END LOOP;
  END IF;
  utl_file.fclose(l_output);
  commit;
EXCEPTION
   WHEN no_data_found THEN null;
   WHEN OTHERS THEN
      vc_errm := SQLERRM;
      ROLLBACK;
      RAISE_APPLICATION_ERROR(-20000, 'PACK file_handling: '||p_filename||' '||p_dirname||' '||vc_errm);
END blob2file;

Auf diese Weise lassen sich alle Datei-Typen platzieren. Erprobt wurde das geschilderte Verfahren mit CSV-Dateien, die für einen Einlese-Prozess bereitgestellt werden müssen, mit RPT-Dateien für Oracle-Reports, aber auch mit Word- und PDF-Dateien. Eine maximale Dateigröße wird höchstens durch die Begrenzungen einer 32-Bit-Umgebung gesetzt.

Da UTL_FILE Dateien nicht nur schreiben, sondern auch lesen kann, könnte es auch genutzt werden, um Dateiinhalte des Betriebssystems zum Download anzubieten. Wer auf einer Standard- oder gar auf einer Enterprise Edition arbeitet, kann noch mehr mit dem Betriebssystem interagieren - dazu mehr im Tipp Betriebssystem-Kommandos und Dateisystemzugriffe mit Application Express. Daneben dürften die Tutorials zum Datei-Upload als auch zum Datei-Download interessant sein.

Zurück zur Community-Seite