Um auf Dateien zugreifen zu können müssen bestimmte Voraussetzungen gegeben sein:
Angenommen
Bedingung 1 und 2 sind in der Regel bereits erfüllt, wenn nicht explizit Zugangsbeschränkungen erteilt wurden.
Das Directory muss von einem DBA erstellt werden, oder einem User, der die entsprechende Berechtigung hat
CREATE OR REPLACE DIRECTORY doc AS 'D:\marcus\ora\doc'; GRANT READ, WRITE ON DIRECTORY doc TO hr;
Wichtig: beim Erstellen wurde hier zwar der Directoryname klein geschrieben, wird von Oracle aber immer in Großbuchstaben angelegt und muss deshalb mit 'DOC' referenziert werden.
Die vorhandenen Directories und die zugewiesenen Berechtigungen können über all_directories geprüft werden. Die Bedeutung der Java Privilegien wird weiter unten erläutert.
SELECT ad.directory_name "Directory Name" ,ad.directory_path "Path" ,atp.grantee "Grantee" ,atp.privilege "Privilege" ,ujp.type_name "Java Priv. Typ" ,ujp.action "Java Priv." FROM all_directories ad JOIN all_tab_privs atp ON (ad.directory_name = atp.table_name) LEFT JOIN user_java_policy ujp ON (ujp.name = ad.directory_path) ORDER BY ad.directory_name;
Beim Öffnen einer Datei wird ein Handler erzeugt, mit dem man später auf die Datei zugreifen kann
utl_file.FILE_TYPE
Um eine Datei lesen zu können muss man ihren Namen kennen; eine Möglichkeit dies zu umgehen beschreibe ich später.
DECLARE v_fileHandle utl_file.FILE_TYPE; v_line VARCHAR2(32767); BEGIN v_fileHandle := utl_file.fopen('DOC','testfile.txt','R',32767);
In diesem Beispiel wird im Directory DOC eine Datei namens testfile.txt für lesenden Zugriff geöffnet.
utl_file.get_line (
v_fileHandle
,v_line
,32767
);
Dies liest eine Zeile mit der maximal möglichen Länge. Wird eine kleinere Zahl angegeben, dann wird möglicherweise nur ein Teil der Zeile gelesen, der nächste Lesevorgang beginnt dann nicht in der nächsten Zeile, sondern setzt am vorherigen an.
utl_file.fclose(v_fileHandle); END; /
Eine Datei kann auch direkt in ein CLOB geladen werden, ohne sie Zeile für Zeile zu lesen. Hierzu gibt es dbms_lob.loadClobFromfile.
Eine mögliche Implementierung ist die Prozedur getClobFromFile in dem unten zum Herunterladen angebotenen Package mam_file.
DECLARE v_fileHandle utl_file.FILE_TYPE; BEGIN v_fileHandle := utl_file.fopen('DOC','testfile.txt','W',32767);
Wird die Datei zum Schreiben geöffnet, wird eine leere Datei erzeugt. Besteht die Datei bereits wird sie ohne Fehlermeldung überschrieben. Um dies zu vermeiden kann mit utl_file.fgetattr die Existenz geprüft werden
Um Text an eine bestehende Datei anzuhängen kann die Option 'A' (append) beim Öffnen gewählt werden.
utl_file.put_line ( v_fileHandle ,'Lorem ipsum' );
utl_file.put_line schreibt den übergebenen Text und hängt anschließend einen Zeilenumbruch an. Soll in der gleichen Zeile weiterer Text angefügt werden, dann erfolgt das Schreiben mit utl_file.put
Um ein CLOB in eine Datei zu schreiben kann man das zeilenweise über utl_file machen, oder man verwendet
dbms_xslprocessor.clob2file ( v_clob ,'DOC' ,'<filename>' ,);
Für eine Diskussion was es mit verschiedenen Zeichensätzen auf sich hat siehe auch
Wie oben beschrieben bietet Oracle von sich aus keine Möglichkeit den Inhalt eines Verzeichnisses zu lesen.
Abgesehen von der XE Version steht in der Datenbank eine JVM zur Verfügung, die es erlaubt mit Java Mitteln die gewünschten Informationen auszulesen.
Zunächst muss dem User vom DBA der Zugriff über Java erlaubt werden
BEGIN dbms_java.grant_permission ( 'HR'--User ,'SYS:java.io.FilePermission'--Dateizugriff ,'D:\marcus\ora\doc'--Pfad! nicht Directory Name ,'read'--Berechtigung ); END; /
Anschließend wird der Code zum Zugriff eingespielt. Eine Implementierung kann zusammen mit weiteren Dateifunktionen heruntergeladen werden.
Ein Zugriff auf ein Verzeichnis mit diesem Package kann so aussehen:
DECLARE v_directory all_directories.directory_name%TYPE := 'DOC'; tab_filelist mam_file.t_filelist; BEGIN --Lies alle Dateien mit der Endung .xml tab_filelist := mam_file.getFilelist(v_directory,'.xml'); IF tab_filelist.COUNT > 0 THEN <<loop_alleDateien>> FOR i IN tab_filelist.FIRST .. tab_filelist.LAST LOOP -- Tu irgendwas mit den Dateien END LOOP loop_alleDateien; END IF; END; /
Eine simple Alternative, wenn du nur ein Verzeichnis auslesen willst: mam_file_java_util
Es genügt ein Select um Dateiattribute wie Name, Änderungsdatum und Größe auszulesen.
SELECT * FROM TABLE(mam_file_java_util.ls('MY_DIRECTORY')) WHERE name LIKE 'T%' AND modified >= SYSDATE - 3 AND file_size > 10;
Zusätzlich können Dateien verschoben oder gelöscht werden. Warum, wenn man es auch mit utl_file kann?
Beim Kopieren binärer Dateien kann es zu Problemen kommen, da ist Java überlegen.
Das Löschen habe ich implementiert, weil auf einen Windows CIFS-Share, der auf den Linux-DB-Server gemountet war, das Problem auftauchte, dass Dateien mit utl_file scheinbar gelöscht wurden, in Wirklichkeit aber nur das hidden-Flag gesetzt wurde.
Bis heute konnte ich nicht ergründen woran das lag. Mit Java funktioniert es reibungslos
Das verlinkte Package enthält Hilfsfunktionen für Dateizugriffe.
Hinweis: In dem Package wird bei Dateizugriffen immer geprüft, ob die notwendigen Berechtigungen vorliegen. Bei häufigem Öffnen/Schließen ist das nicht notwendig. Es kann vor dem ersten Zugriff direkt geprüft werden, die weiteren Zugriffe erfolgen dann direkt mit utl_file.
Zur Verwendung mit der XE11-Edition müssen in mam_file die Zugriffe auf die Funktionen zum Auflisten der Ordnerinhalte gelöscht werden, da diese Version kein Java enthält!