Java I/O

+ andere TechDocs
+ Netzwerkfunktionen
+ FTP
+ E-Mail
+




Inhalt

  1. I/O-Grundlagen
  2. Programmierbeispiel: Kopiere Datei
  3. Programmierbeispiel: Liste rekursiv Dateien in Verzeichnis inkl. Unterverzeichnissen auf
  4. Programmierbeispiel: Suche rekursiv Dateien mit Dateinamens-Pattern
  5. Programmierbeispiel: Suche nach einem Suchtext rekursiv in Dateien
  6. Programmierbeispiel: Resource --> URL --> URI --> Path im Dateisystem
  7. Programmierbeispiel: Zip
  8. PID-Ermittlung unter Windows und Linux
  9. Start des Webbrowsers und Anzeige besonderer Unicode-Zeichen
  10. Neue Klassen und Methoden ab Java 7 und Java 8
  11. Weitere Programmierbeispiele mit I/O


I/O-Grundlagen

import java.io.*; Die mit ...Stream endenden Klassen lesen und schreiben Binärdaten und die mit ...Reader/...Writer endenden Textzeichen.
BufferedReader in = new BufferedReader(
  new InputStreamReader( System.in ) );
String s = in.readLine();
Texteingabe von Tastatur.
try( BufferedReader in = new BufferedReader(
     new InputStreamReader(
     new FileInputStream("MyFile.txt"), "UTF-8")) ) {
  String line;
  while((line = in.readLine()) != null) {
    // ...
  }
}
Text lesen aus Datei.
Beim InputStreamReader möglichst immer das Encoding angeben (im Beispiel "UTF-8").

try( BufferedWriter out = new BufferedWriter(
     new OutputStreamWriter(
     new FileOutputStream("MyFile.txt"), "UTF-8")) ) {
  // s = ...
  out.write( s );
  out.newLine();
}
Text schreiben in Datei.
'FileOutputStream( "MyFile.txt", true )' hängt an bestehende Datei an (append).
Beim OutputStreamWriter möglichst immer das Encoding angeben (im Beispiel "UTF-8").

try( ObjectInputStream in =
     new ObjectInputStream(
     new FileInputStream( "MyObjs.dat" ) ) ) {
  MyClass myObj = (MyClass) in.readObject();
}
Objekt lesen aus Datei.
try( ObjectOutputStream out =
     new ObjectOutputStream(
     new FileOutputStream( "MyObjs.dat" ) ) ) {
  out.writeObject( myObj );
  out.flush();
}
Objekt schreiben in Datei.
byte[] buffer = { 72, 65, 76, 76, 79 };
byte[] result =
  new ByteArrayInputStream( buffer ).readAllBytes();
System.out.println( Arrays.toString( result ) );
InputStream.readAllBytes() gibt es ab Java 9.
byte[] buffer = { 72, 65, 76, 76, 79 };
new ByteArrayInputStream( buffer ).transferTo(
  System.out );
InputStream.transferTo() gibt es ab Java 9.
ZipInputStream, ZipOutputStream, GZIPInputStream, GZIPOutputStream
Komprimieren/Dekomprimieren.
class MyClass implements Serializable
{ String s; // stored
  transient int i; // transient: not stored
}
Serialisierung (Speicherung) eines Objekts ermöglichen.
Runtime.getRuntime().exec(
  "C:\\Windows\\Calc.exe" );
Kommando in getrenntem Betriebssystem-Prozess starten.

Falls Sie einen OutputStream in einen InputStream wandeln müssen, hilft vielleicht folgendes Code-Snippet:

   PipedInputStream  in  = new PipedInputStream();
   PipedOUtputStream out = new PipedOutputStream( in );
   new Thread(
      new Runnable() {
         public void run() {
            // class1 muss eine beliebige putDataOnOutputStream()-Methode implementieren:
            class1.putDataOnOutputStream( out );
         }
      }
   ).start();
   // class2 muss eine beliebige processDataFromInputStream()-Methode implementieren:
   class2.processDataFromInputStream( in );



Programmierbeispiel: Kopiere Datei

import java.io.*;
import java.nio.channels.FileChannel;
import java.nio.file.*;

public class FileCopy
{
   public static void main( String[] args ) throws IOException
   {
      if( 2 != args.length )
         System.out.println( "Error: Parameter missing:\n  java FileCopy InputFile OutputFile\n" );
      else
         copy( args[0], args[1], false, false );
   }

   // Seit Java JDK 7 bevorzugte Methode
   public static void copySinceJava17( File fin, File fout, boolean append ) throws IOException
   {
      if( append ) {
         try( OutputStream out = Files.newOutputStream( fout.toPath(), StandardOpenOption.CREATE, StandardOpenOption.APPEND ) ) {
            Files.copy( fin.toPath(), out );
         }
      } else {
         Files.copy( fin.toPath(), fout.toPath(), StandardCopyOption.REPLACE_EXISTING );
      }
   }

   // Seit Java JDK 1.4 bevorzugte Methode
   public static void copySinceJava14( File fin, File fout, boolean append ) throws IOException
   {
      FileChannel inChannel  = new FileInputStream(  fin ).getChannel();
      FileChannel outChannel = new FileOutputStream( fout, append ).getChannel();
      try {
         inChannel.transferTo( 0, inChannel.size(), outChannel );
      } finally {
         if( inChannel  != null ) try { inChannel.close();  } catch( IOException ex ) {/*ok*/}
         if( outChannel != null ) try { outChannel.close(); } catch( IOException ex ) {/*ok*/}
      }
   }

   // Bis Java JDK 1.3 zu verwendende Methode
   public static void copyUptoJava13( File fin, File fout, boolean append ) throws IOException
   {
      int    len  = 32768;
      byte[] buff = new byte[(int) Math.min( len, fin.length() )];
      FileInputStream  fis = null;
      FileOutputStream fos = null;
      try {
         fis = new FileInputStream( fin );
         fos = new FileOutputStream( fout, append );
         while( 0 < (len = fis.read( buff )) )
            fos.write( buff, 0, len );
      } finally {
         try { if( fos != null ) fos.close(); } catch( IOException ex ) {/*ok*/}
         try { if( fis != null ) fis.close(); } catch( IOException ex ) {/*ok*/}
      }
   }

   // Zusaetzliche Fehlerueberpruefungen
   public static void copy( String fromFileName, String toFileName,
         boolean overwriteIfExists, boolean append ) throws IOException
   {
      File fromFile = new File( fromFileName );
      File toFile   = new File( toFileName );

      if( !fromFile.exists() )
         throw new IOException( "Fehler: Quelldatei fehlt: " + fromFileName );
      if( !fromFile.isFile() )
         throw new IOException( "Fehler: Quelle ist ein Verzeichnis: " + fromFileName );
      if( !fromFile.canRead() )
         throw new IOException( "Fehler: Keine Leseberechtigung fuer Quelldatei: " + fromFileName );

      if( toFile.isDirectory() )
         toFile = new File( toFile, fromFile.getName() );

      if( toFile.exists() ) {
         if( !overwriteIfExists && !append )
            throw new IOException( "Fehler: Zieldatei existiert bereits: " + toFileName );
         if( !toFile.canWrite() )
            throw new IOException( "Fehler: Keine Schreibberechtigung fuer Zieldatei: " + toFileName );
      } else {
         String parent = toFile.getParent();
         if( parent == null )
            parent = System.getProperty( "user.dir" );
         File dir = new File( parent );
         if( !dir.exists() )
            throw new IOException( "Fehler: Zielverzeichnis existiert nicht: " + parent );
         if( dir.isFile() )
            throw new IOException( "Fehler: Ziel ist kein Verzeichnis: " + parent );
         if( !dir.canWrite() )
            throw new IOException( "Fehler: Keine Schreibberechtigung fuer Zielverzeichnis: " + parent );
      }

      copySinceJava17( fromFile, toFile, append );
   }
}


Programmierbeispiel: Liste rekursiv Dateien in Verzeichnis inkl. Unterverzeichnissen auf

Konventionelle Variante mit File.listFiles():

import java.io.File;

public class GetFilesInDir1
{
   public static void main( String[] args )
   {
      System.out.println( "\nZeige rekursiv alle Dateien in einem Verzeichnis und den Unterverzeichnissen.\n" +
                          "Das Startverzeichnis wird als Kommandozeilenparameter uebergeben.\n" );
      String startDir = ".";
      if( args != null && args.length > 0 ) {
         startDir = args[0];
      }
      System.out.println( getFilesInDirToString( startDir, null, null, null ) );
   }

   public static String getFilesInDirToString( String dir, String spacesStart, String spacesAdd, String lineSeparator )
   {
      if( lineSeparator == null ) { lineSeparator = System.getProperty( "line.separator" ); }
      if( spacesStart   == null ) { spacesStart   = ""; }
      if( spacesAdd     == null ) { spacesAdd     = "    "; }
      if( dir == null || dir.trim().length() == 0 ) {
         return lineSeparator + "Fehler: Verzeichnis muss uebergeben werden." + lineSeparator;
      }
      File[] entries = (new File( dir )).listFiles();
      if( entries == null ) {
         return lineSeparator + "Fehler: '" + dir + "' ist kein Verzeichnis." + lineSeparator;
      }
      if( entries.length == 0 ) {
         return spacesStart + "'" + dir + "' enthaelt keine Dateien oder Verzeichnisse." + lineSeparator;
      }
      StringBuilder sb = new StringBuilder( "" );
      for( int i = 0; i < entries.length; i++ ) {
         if( entries[i].isFile() ) {
            sb.append( spacesStart ).append( entries[i] ).append( lineSeparator );
         }
      }
      for( int i = 0; i < entries.length; i++ ) {
         if( entries[i].isDirectory() ) {
            sb.append( lineSeparator )
              .append( spacesStart ).append( "Verzeichnis " ).append( entries[i].toString() ).append( lineSeparator )
              .append( getFilesInDirToString( entries[i].toString(), spacesStart + spacesAdd, spacesAdd, lineSeparator ) );
         }
      }
      return sb.toString();
   }
}

NIO-Variante mit Files.walkFileTree():

import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;

public class GetFilesInDir2
{
   public static void main( String[] args ) throws IOException
   {
      System.out.println( "\nZeige rekursiv alle Dateien in einem Verzeichnis und den Unterverzeichnissen.\n" +
                          "Das Startverzeichnis wird als Kommandozeilenparameter uebergeben.\n" );
      String startDir = ".";
      if( args != null && args.length > 0 ) {
         startDir = args[0];
      }
      getFilesInDir( startDir );
   }

   public static void getFilesInDir( String startDir ) throws IOException {
      Files.walkFileTree( Paths.get( startDir ), new SimpleFileVisitor<Path>() {
         @Override
         public FileVisitResult visitFile( Path path, BasicFileAttributes bfa ) {
            System.out.println( path + " (Dateigroesse: " + bfa.size() + " Bytes)" );
            return FileVisitResult.CONTINUE;
         }
      });
   }
}


Programmierbeispiel: Suche rekursiv Dateien mit Dateinamens-Pattern

import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;

public class DateiSuche
{
   public static void main( String[] args ) throws IOException
   {
      System.out.println( "\nSuche rekursiv nach Dateien, welche einem Dateinamens-Pattern entsprechen.\n" +
            "Zusaetzlich kann optional ein Unterverzeichnis angegeben werden, das ausgeschlossen werden soll.\n" +
            "Kommandozeilenparameter: DateinamensPattern StartVerzeichnis AuschlussVerzeichnis.\nBeispiele:\n" +
            "   * .\n" +
            "   *.prop* /MeinStartDir MeinExcludeDir\n" +
            "   *.{txt,java,class} . target\n" );
      String pattern    = ( args.length > 0 ) ? args[0] : "*";
      String startDir   = ( args.length > 1 ) ? args[1] : ".";
      String excludeDir = ( args.length > 2 ) ? args[2] : null;
      System.out.println( sucheDateien( pattern, startDir, excludeDir ) );
   }

   public static List<Path> sucheDateien( String pattern, String startDir, String excludeDir ) throws IOException
   {
      List<Path> result = new ArrayList<>();
      PathMatcher pathMatcher = FileSystems.getDefault().getPathMatcher( "glob:" + pattern );
      Files.walkFileTree( Paths.get( startDir ), new SimpleFileVisitor<Path>() {
         boolean excludeDirFound;
         @Override
         public FileVisitResult visitFile( Path path, BasicFileAttributes bfa ) {
            excludeDirFound = false;
            if( excludeDir != null && excludeDir.length() > 0 ) {
               path.getParent().spliterator().forEachRemaining( subpath -> {
                  if( excludeDir.equals( subpath.toString() ) ) {
                     excludeDirFound |= true;
                  }
               } );
            }
            if( !excludeDirFound && pathMatcher.matches( path.getFileName() ) ) {
               result.add( path );
            }
            return FileVisitResult.CONTINUE;
         }
      } );
      return result;
   }
}

Alternativ (mit eingeschränkter Funktionalität):

/** Liste Dateien auf, gefiltert per Dateiname mit Joker */
private static List<Path> listFilesWithJoker( String dir, String filenameWithJoker )
{
   List<Path> paths = new ArrayList<>();
   try( DirectoryStream<Path> dirStream = Files.newDirectoryStream( Paths.get( dir ), filenameWithJoker ) ) {
      dirStream.forEach( paths::add );
   } catch( IOException ex ) {
      LOGGER.error( "Fehler bei der Suche nach " + dir + "/" + filenameWithJoker + "-Dateien. ", ex );
   }
   return paths;
}


Programmierbeispiel: Suche nach einem Suchtext rekursiv in Dateien

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;

public class TextSucheInDateien
{
   public static void main( String[] args ) throws IOException
   {
      System.out.println( "\nSuche nach einem Suchtext rekursiv in Dateien, welche einem Dateinamens-Pattern entsprechen.\n" +
            "Zusaetzlich kann optional ein Unterverzeichnis angegeben werden, das ausgeschlossen werden soll.\n" +
            "Kommandozeilenparameter: Suchtext DateinamensPattern StartVerzeichnis AuschlussVerzeichnis.\nBeispiele:\n" +
            "   MeinSuchText * .\n" +
            "   MeinSuchText *.prop* /MeinStartDir MeinExcludeDir\n" +
            "   walkFileTree *.{xml,java} . target\n" );
      String suchtext   = ( args.length > 0 ) ? args[0] : "walkFileTree";
      String pattern    = ( args.length > 1 ) ? args[1] : "*";
      String startDir   = ( args.length > 2 ) ? args[2] : ".";
      String excludeDir = ( args.length > 3 ) ? args[3] : null;
      List<Path> pathes = sucheDateien( pattern, startDir, excludeDir );
      List<List<String>> result = sucheTextInDateien( suchtext, pathes );
      for( List<String> ss : result ) {
         System.out.println( ss.get( 0 ) + ": " + ss.get( 1 ) );
      }
   }

   public static List<Path> sucheDateien( String pattern, String startDir, String excludeDir ) throws IOException
   {
      List<Path> result = new ArrayList<>();
      PathMatcher pathMatcher = FileSystems.getDefault().getPathMatcher( "glob:" + pattern );
      Files.walkFileTree( Paths.get( startDir ), new SimpleFileVisitor<Path>() {
         boolean excludeDirFound;
         @Override
         public FileVisitResult visitFile( Path path, BasicFileAttributes bfa ) {
            excludeDirFound = false;
            if( excludeDir != null && excludeDir.length() > 0 ) {
               path.getParent().spliterator().forEachRemaining( subpath -> {
                  if( excludeDir.equals( subpath.toString() ) ) {
                     excludeDirFound |= true;
                  }
               } );
            }
            if( !excludeDirFound && pathMatcher.matches( path.getFileName() ) ) {
               result.add( path );
            }
            return FileVisitResult.CONTINUE;
         }
      } );
      return result;
   }

   public static List<List<String>> sucheTextInDateien( String suchtext, List<Path> pathes ) throws IOException
   {
      List<List<String>> result = new ArrayList<>();
      String suchtextLowercase = suchtext.toLowerCase();
      for( Path p : pathes ) {
         Files.lines( p, StandardCharsets.UTF_8 ).forEach( line -> {
            if( line.toLowerCase().contains( suchtextLowercase ) ) {
               result.add( Arrays.asList( p.toString(), line ) );
            }
         });
      }
      return result;
   }
}


Programmierbeispiel: Resource --> URL --> URI --> Path im Dateisystem

import java.io.*;
import java.net.*;
import java.nio.file.Files;

/**
 * Resource --> URL --> URI --> Path im Dateisystem
 * (insbesondere wenn der Ressource-Pfad Leerzeichen enthaelt)
 */
public class RessourcePfadMitLeerzeichen
{
   public static void main( String[] args ) throws IOException, URISyntaxException
   {
      // Ressource-Datei im Ressource-Verzeichnis (z.B. in: "src/main/resources/Leerzeichen im Ressource-Pfad"):
      String ressourceDateiPfad = "Leerzeichen im Ressource-Pfad/Dateiname mit Leerzeichen.txt";

      // URL zu Ressource-Datei (enthaelt "%20" statt Leerzeichen):
      URL ressourceUrl = RessourcePfadMitLeerzeichen.class.getResource( ressourceDateiPfad );
      if( ressourceUrl == null ) {
         ressourceDateiPfad = "/" + ressourceDateiPfad;
         ressourceUrl = RessourcePfadMitLeerzeichen.class.getResource( ressourceDateiPfad );
      }
      if( ressourceUrl == null ) {
         throw new FileNotFoundException( "Ressource '" + ressourceDateiPfad + "' fehlt" );
      }

      // URL --> URI (enthaelt auch "%20" statt Leerzeichen):
      URI ressourceUri = ressourceUrl.toURI();

      // URL-Decoded und URI-Path (enthalten Leerzeichen statt "%20"):
      String urlDecoded = URLDecoder.decode( ressourceUrl.toString(), StandardCharsets.UTF_8.toString() );
      String uriPath    = ressourceUrl.toURI().getPath();

      // Ergebnisse:
      System.out.println( "" );
      System.out.println( "Ressource-Dateipfad: " + ressourceDateiPfad );
      System.out.println( "Ressource-URL:       " + ressourceUrl );
      System.out.println( "Ressource-URI:       " + ressourceUri );
      System.out.println( "URL-Decoded:         " + urlDecoded );
      System.out.println( "URI-Path:            " + uriPath );

      // ResourceAsStream funktioniert sowohl im Dateisystem als auch in Jar:
      System.out.print( "Dateiinhalt per ResourceAsStream:     " );
      try( InputStream is = RessourcePfadMitLeerzeichen.class.getResourceAsStream( ressourceDateiPfad ) ) {
         try( BufferedReader rd = new BufferedReader( new InputStreamReader( is, StandardCharsets.UTF_8 ) ) ) {
            System.out.println( rd.readLine() );
         }
      }

      // URI-Path funktioniert im Dateisystem, aber nicht in Jar:
      System.out.print( "Dateiinhalt aus Datei im Dateisystem: " );
      if( uriPath == null ) {
         System.out.println( "-- nicht moeglich: URI-Path ist null --" );
      } else {
         Files.lines( new File( uriPath ).toPath(), StandardCharsets.UTF_8 ).forEach( System.out::println );
      }
   }
}

Wenn Sie eine entsprechende Ressource-Datei in dem angegebenen Ressource-Verzeichnis anlegen, und das Java-Programm direkt ausführen, erhalten Sie beispielsweise:

Ressource-Dateipfad: /Leerzeichen im Ressource-Pfad/Dateiname mit Leerzeichen.txt
Ressource-URL:       file:/D:/MeinWorkspace/Ressource-Pfad%20mit%20Leerzeichen/target/classes/Leerzeichen%20im%20Ressource-Pfad/Dateiname%20mit%20Leerzeichen.txt
Ressource-URI:       file:/D:/MeinWorkspace/Ressource-Pfad%20mit%20Leerzeichen/target/classes/Leerzeichen%20im%20Ressource-Pfad/Dateiname%20mit%20Leerzeichen.txt
URL-Decoded:         file:/D:/MeinWorkspace/Ressource-Pfad mit Leerzeichen/target/classes/Leerzeichen im Ressource-Pfad/Dateiname mit Leerzeichen.txt
URI-Path:            /D:/MeinWorkspace/Ressource-Pfad mit Leerzeichen/target/classes/Leerzeichen im Ressource-Pfad/Dateiname mit Leerzeichen.txt
Dateiinhalt per ResourceAsStream:     Dateiinhalt in UTF-8 mit Leerzeichen und äöü߀
Dateiinhalt aus Datei im Dateisystem: Dateiinhalt in UTF-8 mit Leerzeichen und äöü߀

Wenn Sie stattdessen die kompilierte Java-Klasse zusammen mit der Ressource-Datei in eine Jar-Datei verpacken, und dann diese Jar-Datei ausführen, erhalten Sie beispielsweise:

Ressource-Dateipfad: /Leerzeichen im Ressource-Pfad/Dateiname mit Leerzeichen.txt
Ressource-URL:       jar:file:/D:/MeinWorkspace/Ressource-Pfad%20mit%20Leerzeichen/target/RessourcePfadMitLeerzeichen.jar!/Leerzeichen%20im%20Ressource-Pfad/Dateiname%20mit%20Leerzeichen.txt
Ressource-URI:       jar:file:/D:/MeinWorkspace/Ressource-Pfad%20mit%20Leerzeichen/target/RessourcePfadMitLeerzeichen.jar!/Leerzeichen%20im%20Ressource-Pfad/Dateiname%20mit%20Leerzeichen.txt
URL-Decoded:         jar:file:/D:/MeinWorkspace/Ressource-Pfad mit Leerzeichen/target/RessourcePfadMitLeerzeichen.jar!/Leerzeichen im Ressource-Pfad/Dateiname mit Leerzeichen.txt
URI-Path:            null
Dateiinhalt per ResourceAsStream:     Dateiinhalt in UTF-8 mit Leerzeichen und äöü߀
Dateiinhalt aus Datei im Dateisystem: -- nicht moeglich: URI-Path ist null --


Programmierbeispiel: Zip

import java.io.*;
import java.nio.charset.*;
import java.util.*;
import java.util.zip.*;

/**
 * Hilfsklasse zum Zippen und Unzippen.
 */
public class ZipUtil
{
   /**
    * Interface fuer Unzip-Callback-Objekt zur Verarbeitung einzelner ZipEntries zu OutputStreams.
    */
   public interface UnzippedEntryInOutputStream
   {
      OutputStream verarbeiteUnzippedEntry( String name ) throws IOException;
   }

   /**
    * Interface fuer Unzip-Callback-Objekt zur Verarbeitung einzelner ZipEntries aus Entry-InputStreams.
    */
   public interface UnzippedEntryFromInputStream
   {
      void verarbeiteUnzippedEntry( String name, InputStream instreamForUnzipped ) throws IOException;
   }

   /**
    * Zip: Einen InputStream in einen OutputStream zippen.<br>
    * Beide Streams werden mit close() geschlossen.
    * @param instreamForZip  InputStream, der gezipped werden soll.
    * @param outstreamZipped OutputStream, gezipped.
    * @param origFilename    Originaldateiname (wird in der Zipdatei gespeichert mit UTF-8-Character-Encoding).
    */
   public static void zipStream( InputStream instreamForZip, OutputStream outstreamZipped, String origFilename ) throws IOException
   {
      zipStream( instreamForZip, outstreamZipped, origFilename, StandardCharsets.UTF_8 );
   }

   /**
    * Zip: Einen InputStream in einen OutputStream zippen.<br>
    * Beide Streams werden mit close() geschlossen.
    * @param instreamForZip  InputStream, der gezipped werden soll.
    * @param outstreamZipped OutputStream, gezipped.
    * @param origFilename    Originaldateiname (wird in der Zipdatei gespeichert).
    * @param zipCharEncoding Charset fuer Character-Encoding des Zip-Entry-Namens, normalerweise StandardCharsets.UTF_8
    */
   public static void zipStream( InputStream instreamForZip, OutputStream outstreamZipped, String origFilename, Charset zipCharEncoding ) throws IOException
   {
      IOException ex = null;
      try {
         byte[] buf = new byte[65536];
         int len;
         try( ZipOutputStream zout = new ZipOutputStream( new BufferedOutputStream( outstreamZipped ), zipCharEncoding ) ) {
            zout.putNextEntry( new ZipEntry( origFilename ) );
            while( (len = instreamForZip.read( buf )) > 0 ) {
               zout.write( buf, 0, len );
            }
            try { zout.closeEntry(); } catch( IOException e ) { ex = e; }
            try { zout.finish(); }     catch( IOException e ) { if( ex == null ) { ex = e; } }
            try { zout.flush(); }      catch( IOException e ) { if( ex == null ) { ex = e; } }
         }
      } finally {
         try { instreamForZip.close(); } catch( IOException e ) { if( ex == null ) { ex = e; } }
      }
      if( ex != null ) { throw ex; }
   }

   /**
    * Unzip: Einen gezippten InputStream in einen InputStream entzippen.<br>
    * Die Streams werden mit close() geschlossen.
    * @param  instreamZipped  InputStream der gezippten Datei
    * @param  verarbeitung    Callback-Objekt zur Verarbeitung einzelner ZipEntries aus Entry-InputStreams
    * @return Anzahl Entries
    */
   public static long unzipStream( InputStream instreamZipped, UnzippedEntryFromInputStream verarbeitung ) throws IOException
   {
      return unzipStream( instreamZipped, verarbeitung, StandardCharsets.UTF_8 );
   }

   /**
    * Unzip: Einen gezippten InputStream in einen InputStream entzippen.<br>
    * Die Streams werden mit close() geschlossen.
    * @param  instreamZipped  InputStream der gezippten Datei
    * @param  verarbeitung    Callback-Objekt zur Verarbeitung einzelner ZipEntries aus Entry-InputStreams
    * @param  zipCharEncoding Charset fuer Character-Encoding des Zip-Entry-Namens, normalerweise StandardCharsets.UTF_8
    * @return Anzahl Entries
    */
   public static long unzipStream( InputStream instreamZipped, UnzippedEntryFromInputStream verarbeitung, Charset zipCharEncoding ) throws IOException
   {
      long anzahlEntries = 0;
      try( BufferedInputStream in = new BufferedInputStream( instreamZipped );
           ZipInputStream zin = new ZipInputStream( in, zipCharEncoding ) {
               @Override public void close() throws IOException {
                  // Do nothing:
                  // Das Ueberschreiben von close() ist notwendig, um "java.io.IOException: Stream closed" zu vermeiden,
                  // falls in verarbeitung.verarbeiteUnzippedEntry() XML geparst wird,
                  // wegen eines Fehlers im JAXP-XMLStreamReader, siehe http://bugs.sun.com/view_bug.do?bug_id=6539065
               }
            } ) {
         ZipEntry zipEntry;
         while( (zipEntry = zin.getNextEntry()) != null ) {
            verarbeitung.verarbeiteUnzippedEntry( zipEntry.getName(), zin );
            zin.closeEntry();
            anzahlEntries++;
         }
      }
      return anzahlEntries;
   }

   /**
    * Unzip: Einen gezippten InputStream in einen OutputStream entzippen.<br>
    * Die Streams werden mit close() geschlossen.
    * @param  instreamZipped  InputStream der gezippten Datei
    * @param  verarbeitung    Callback-Objekt zur Verarbeitung einzelner ZipEntries zu OutputStreams
    * @return Anzahl Entries
    */
   public static long unzipStream( InputStream instreamZipped, UnzippedEntryInOutputStream verarbeitung ) throws IOException
   {
      return unzipStream( instreamZipped, verarbeitung, StandardCharsets.UTF_8 );
   }

   /**
    * Unzip: Einen gezippten InputStream in einen OutputStream entzippen.<br>
    * Die Streams werden mit close() geschlossen.
    * @param  instreamZipped  InputStream der gezippten Datei
    * @param  verarbeitung    Callback-Objekt zur Verarbeitung einzelner ZipEntries zu OutputStreams
    * @param  zipCharEncoding Charset fuer Character-Encoding des Zip-Entry-Namens, normalerweise StandardCharsets.UTF_8
    * @return Anzahl Entries
    */
   public static long unzipStream( InputStream instreamZipped, UnzippedEntryInOutputStream verarbeitung, Charset zipCharEncoding ) throws IOException
   {
      long anzahlEntries = 0;
      try( ZipInputStream zin = new ZipInputStream( new BufferedInputStream( instreamZipped ), zipCharEncoding ) ) {
         IOException ex = null;
         ZipEntry zipEntry;
         while( ex == null && (zipEntry = zin.getNextEntry()) != null ) {
            OutputStream os = verarbeitung.verarbeiteUnzippedEntry( zipEntry.getName() );
            if( os != null ) {
               try( BufferedOutputStream bos = new BufferedOutputStream( os ) ) {
                  int size;
                  byte[] buffer = new byte[64 * 1024];
                  while( (size = zin.read( buffer, 0, buffer.length )) > 0 ) {
                     bos.write( buffer, 0, size );
                  }
                  try { bos.flush(); } catch( IOException e ) { ex = e; }
               }
               anzahlEntries++;
            }
            zin.closeEntry();
         }
         if( ex != null ) { throw ex; }
      }
      return anzahlEntries;
   }

   /**
    * Aus einem gezippten InputStream den Namen des ersten Eintrags ermitteln.<br>
    * Der InputStream wird mit close() geschlossen.
    * @param  instreamZipped  InputStream der gezippten Datei
    * @return Name des ersten Entries (gespeichert im UTF-8-Character-Encoding).
    */
   public static String getNameOfFirstEntry( InputStream instreamZipped ) throws IOException
   {
      return getNameOfFirstEntry( instreamZipped, StandardCharsets.UTF_8 );
   }

   /**
    * Aus einem gezippten InputStream den Namen des ersten Eintrags ermitteln.<br>
    * Der InputStream wird mit close() geschlossen.
    * @param  instreamZipped  InputStream der gezippten Datei
    * @param  zipCharEncoding Charset fuer Character-Encoding des Zip-Entry-Namens, normalerweise StandardCharsets.UTF_8
    * @return Name des ersten Entries.
    */
   public static String getNameOfFirstEntry( InputStream instreamZipped, Charset zipCharEncoding ) throws IOException
   {
      String entryName = null;
      try( ZipInputStream zin = new ZipInputStream( new BufferedInputStream( instreamZipped ), zipCharEncoding ) ) {
         ZipEntry zipEntry = zin.getNextEntry();
         if( zipEntry != null ) {
            entryName = zipEntry.getName();
         }
      } finally {
         instreamZipped.close();
      }
      return entryName;
   }

   /**
    * Unzip-Callback-Klasse zur Speicherung einzelner ZipEntries in Dateien.
    */
   public static class UnzippedEntryToFile implements UnzippedEntryInOutputStream
   {
      public String             filenamePrefix = "unzipped.";
      public final List<String> filenames = new ArrayList<String>();

      @Override
      public OutputStream verarbeiteUnzippedEntry( String entryName ) throws IOException
      {
         if( entryName == null ) { return null; }
         String filename = entryName;
         if( filename.startsWith( "/" ) ) { filename = filename.substring( 1 ); }
         if( this.filenamePrefix != null ) { filename = this.filenamePrefix + filename; }
         this.filenames.add( filename );
         if( filename.endsWith( "/" ) || filename.endsWith( "\\" ) ) {
            (new File( filename )).mkdirs();
            return null;
         }
         return new FileOutputStream( filename );
      }
   }

   /**
    * Unzip-Callback-Klasse zur Speicherung einzelner ZipEntries in Strings (mit vorgegebener maximaler Laenge).
    */
   public static class UnzippedEntryToString implements UnzippedEntryFromInputStream
   {
      public int                lenMax           = 2048;
      public String             fileCharEncoding = "UTF-8";
      public final List<String> strings          = new ArrayList<String>();

      public UnzippedEntryToString( String fileCharEncoding )
      {
         this.fileCharEncoding = fileCharEncoding;
      }

      @Override
      public void verarbeiteUnzippedEntry( String entryName, InputStream unzippedEntryInstream ) throws IOException
      {
         byte[] buffer = new byte[this.lenMax];
         int len = unzippedEntryInstream.read( buffer );
         this.strings.add( new String( buffer, 0, len, this.fileCharEncoding ) );
      }
   }

   /**
    * Nur fuer manuellen Unzip-Test
    */
   public static void main( String[] args ) throws IOException
   {
      if( args.length > 0 ) {
         UnzippedEntryToFile callback = new UnzippedEntryToFile();
         callback.filenamePrefix = null;
         try( InputStream is = new FileInputStream( args[0] ) ) {
            System.out.println( "Anzahl Entries: " + unzipStream( is, callback, StandardCharsets.UTF_8 ) );
            System.out.println( "Entry-Namen:    " + callback.filenames );
         }
      }
   }
}
import java.io.*;
import java.nio.charset.*;
import org.junit.*;

/**
 * JUnit-Test fuer ZipUtil
 */
public class ZipUtilTest
{
   @Test
   public void testZipUtil() throws IOException
   {
      testZipUtil( StandardCharsets.UTF_8,      StandardCharsets.UTF_8      );
      testZipUtil( StandardCharsets.UTF_8,      StandardCharsets.ISO_8859_1 );
      testZipUtil( StandardCharsets.ISO_8859_1, StandardCharsets.UTF_8      );
      testZipUtil( StandardCharsets.ISO_8859_1, StandardCharsets.ISO_8859_1 );
   }

   public void testZipUtil( Charset fileCharEncoding, Charset zipCharEncoding ) throws IOException
   {
      String targetPath             = "target/";
      String origFilename           = "MeineDatei-äöüß.txt";
      String zipFilename            = origFilename + ".zip";
      String unzippedFilenamePrefix = "unzipped.";
      String meinText               = "BlaBlupp äöüß\u20AC";
      String meinTextCmp            = ( StandardCharsets.UTF_8.equals( fileCharEncoding  ) ) ? meinText : meinText.replace( '\u20AC', '?' );
      int    zipResultSize          = 168;
      if( StandardCharsets.UTF_8.equals( fileCharEncoding ) ) { zipResultSize += 7; }
      if( StandardCharsets.UTF_8.equals( zipCharEncoding  ) ) { zipResultSize += 8; }

      // Zip mit ByteArray:
      try( InputStream           isForZip  = new ByteArrayInputStream( meinText.getBytes( fileCharEncoding ) );
           ByteArrayOutputStream bosZipped = new ByteArrayOutputStream() ) {
         ZipUtil.zipStream( isForZip, bosZipped, origFilename, zipCharEncoding );
         Assert.assertEquals( zipResultSize, bosZipped.size() );
      }

      // Zip mit Datei:
      (new File( targetPath )).mkdir();
      try( BufferedWriter out = new BufferedWriter( new OutputStreamWriter( new FileOutputStream(
            targetPath + origFilename ), fileCharEncoding ) ) ) {
         out.write( meinText );
      }
      try( InputStream  isForZip  = new FileInputStream(  targetPath + origFilename );
           OutputStream fosZipped = new FileOutputStream( targetPath + zipFilename )) {
         ZipUtil.zipStream( isForZip, fosZipped, origFilename, zipCharEncoding );
         File zipFile = new File( targetPath + zipFilename );
         Assert.assertTrue( zipFile.exists() );
         Assert.assertEquals( zipResultSize, zipFile.length() );
      }

      // Unzip aus InputStream zu String:
      ZipUtil.UnzippedEntryToString entryToString = new ZipUtil.UnzippedEntryToString( fileCharEncoding.name() );
      try( InputStream is = new FileInputStream( targetPath + zipFilename ) ) {
         long anzahlEntries = ZipUtil.unzipStream( is, entryToString, zipCharEncoding );
         Assert.assertEquals( 1, anzahlEntries );
         Assert.assertEquals( 1, entryToString.strings.size() );
         Assert.assertEquals( meinTextCmp, entryToString.strings.get( 0 ) );
      }

      // Unzip in OutputStream in Datei:
      ZipUtil.UnzippedEntryToFile entryToFile = new ZipUtil.UnzippedEntryToFile();
      entryToFile.filenamePrefix = targetPath + unzippedFilenamePrefix;
      try( InputStream is = new FileInputStream( targetPath + zipFilename ) ) {
         long anzahlEntries = ZipUtil.unzipStream( is, entryToFile, zipCharEncoding );
         Assert.assertEquals( 1, anzahlEntries );
         Assert.assertEquals( 1, entryToFile.filenames.size() );
         Assert.assertEquals( targetPath + unzippedFilenamePrefix + origFilename, entryToFile.filenames.get( 0 ) );
      }
      try( BufferedReader in = new BufferedReader( new InputStreamReader( new FileInputStream(
            targetPath + unzippedFilenamePrefix + origFilename ), fileCharEncoding ) ) ) {
         Assert.assertEquals( meinTextCmp, in.readLine() );
      }
   }
}

Mit JUnit 4.10 führen Sie aus:

javac -cp .;junit-4.10.jar *.java

java  -cp .;junit-4.10.jar org.junit.runner.JUnitCore ZipUtilTest

Für JUnit 4.12 downloaden Sie junit-4.12.jar und hamcrest-core-1.3.jar und führen aus:

javac -cp .;junit-4.12.jar *.java

java  -cp .;junit-4.12.jar;hamcrest-core-1.3.jar org.junit.runner.JUnitCore ZipUtilTest

Falls Sie beim Unzippen folgende Fehlermeldung erhalten:

java.lang.IllegalArgumentException: MALFORMED
at java.util.zip.ZipCoder.toString

Dann haben Sie wahrscheinlich ein Character-Encoding-Problem beim Aufruf von ZipInputStream.getNextEntry() wegen Umlauten und Sonderzeichen im Zip-Entry-Namen. Erweitern Sie den new ZipInputStream()-Aufruf um den passenden Character-Encoding-Charset, z.B.:

ZipInputStream zin = new ZipInputStream( meinInputStream, StandardCharsets.ISO_8859_1 );

bzw.:

ZipInputStream zin = new ZipInputStream( meinInputStream, StandardCharsets.UTF_8 );

Analoges gilt natürlich ebenso für ZipOutputStream.



PID-Ermittlung unter Windows und Linux

Folgendermaßen kann die Prozess-ID unter Windows und Linux ermittelt werden:


import java.io.IOException;
import java.io.InputStream;

/** PID-Ermittlung (= Prozess-ID) mit Java 8 und Java 9, unter Windows und Linux */
public class GetPID
{
   public static void main( String[] args ) throws IOException, InterruptedException
   {
      System.out.println( getPidJava8() );
      System.out.println( getPidJava9() );
   }

   /** PID-Ermittlung mit Java 8 */
   static long getPidJava8() throws IOException, InterruptedException
   {
      String os = System.getenv( "OS" );
      if( os == null ) { os = "Linux"; }
      System.out.println( "\n" + os );
      // Linux ($PPID = Parent Process ID):
      final String[] commandsLinux   = new String[] { "/bin/sh", "-c", "echo $PPID" };
      // Windows:
      final String[] commandsWindows = new String[] { "powershell",
            "gwmi win32_process | ?{$_.ProcessID -eq $pid} | select ParentProcessID | fw -c 2" };
      final String[] commands = ( os != null && os.toLowerCase().contains( "windows" ) )
                                ? commandsWindows : commandsLinux;
      final Process proc = Runtime.getRuntime().exec( commands );
      if( proc.waitFor() == 0 ) {
         try( final InputStream in = proc.getInputStream() ) {
            final int available = in.available();
            final byte[] outputBytes = new byte[available];
            in.read( outputBytes );
            final String pid = new String( outputBytes );
            return Long.parseLong( pid.trim() );
         }
      }
      throw new IllegalStateException( "PID kann nicht ermittelt werden" );
   }

   /** PID-Ermittlung mit Java 9 */
   static long getPidJava9()
   {
      return ProcessHandle.current().pid();
   }
}


Start des Webbrowsers und Anzeige besonderer Unicode-Zeichen

Folgendermaßen kann der Webbrowser gestartet werden und darin Text mit besonderen Unicode-Zeichen angezeigt werden:


import java.awt.Desktop;
import java.io.*;

/** Start des Webbrowsers und darin Anzeige besonderer Unicode-Zeichen */
public class DesktopBrowser
{
   public static void main( String[] args ) throws IOException
   {
      String textKonsole = "\nText mit besonderen Unicode-Zeichen: \u25D0" +
            toSurrogates( 0x1f605 ) + toSurrogates( 0x1f310 ) + toSurrogates( 0x1f575 ) + toSurrogates( 0x1F917 );
      String textHtml = "<html><body><h1>Text mit besonderen Unicode-Zeichen: " +
            "&#x25D0 &#x1f605 &#x1f310 &#x1f575 &#x1F917" + "</h1></body></html>";

      System.out.println( textKonsole );

      zeigeImBrowser( erstelleTemporaereDatei( textHtml, "Unicode-Webseite-", ".html" ) );
   }

   private static String toSurrogates( int codePoint )
   {
      return new String( new char[] { Character.highSurrogate( codePoint ), Character.lowSurrogate( codePoint ) } );
   }

   private static File erstelleTemporaereDatei( String text, String dateinamePrefix, String dateinameSuffix ) throws IOException
   {
      File tmpFile = File.createTempFile( dateinamePrefix, dateinameSuffix );
      try( FileOutputStream     fos = new FileOutputStream( tmpFile );
           BufferedOutputStream bos = new BufferedOutputStream( fos ) ) {
         bos.write( text.getBytes() );
         bos.flush();
      }
      return tmpFile;
   }

   private static void zeigeImBrowser( File htmlFile ) throws IOException
   {
      if( Desktop.isDesktopSupported() ) {
         Desktop desktop = Desktop.getDesktop();
         if( desktop != null && desktop.isSupported( Desktop.Action.BROWSE ) ) {
            desktop.browse( htmlFile.toURI() );
         }
      }
   }
}


Neue Klassen und Methoden ab Java 7 und Java 8

Ab Java 7 gibt es weitere File-I/O-Klassen und -Methoden, die vieles vereinfachen, siehe bei OpenJDK oder java.nio.file, zum Beispiel:
FileSystems.getDefault(), FileSystem, Path.getFileSystem(), Path, Paths, Files, Files.deleteIfExists(), Files.copy(), Files.move(), Files.newDirectoryStream(), DirectoryStream, Files.walkFileTree(), FileVisitor, BasicFileAttributes, FileAttribute, WatchService, Path.register(), AsynchronousFileChannel.

Beachten Sie, dass es die Klasse FileSystem ab Java 7 doppelt gibt: als abstrakte (normalerweise nicht sichtbare) java.io.FileSystem und als java.nio.file.FileSystem.

In Java 8 sind viele weitere neue File-I/O-Klassen und -Methoden im Streaming-API hinzugekommen, siehe: Java 8 Stream-API.



Weitere Programmierbeispiele mit I/O





Weitere Themen: andere TechDocs | Netzwerkfunktionen | FTP | E-Mail
© 1998-2007 Torsten Horn, Aachen