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 ); |
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 ); } }
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; } }); } }
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; }
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; } }
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 --
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.
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(); } }
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: " + "◐ 😅 🌐 🕵 🤗" + "</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() ); } } } }
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.