Gids voor Java FileChannel

1. Overzicht

In deze korte tutorial kijken we naar de FileChannel klasse voorzien in de Java NIO bibliotheek. We zullen bespreken hoe gegevens te lezen en te schrijven met FileChannel en ByteBuffer.

We zullen ook de voordelen van het gebruik van FileChannel en enkele van zijn andere functies voor bestandsmanipulatie.

2. Voordelen van FileChannel

De voordelen van FileChannel omvatten:

  • Lezen en schrijven op een specifieke positie in een bestand
  • Een sectie van een bestand rechtstreeks in het geheugen laden, wat efficiënter kan zijn
  • We kunnen bestandsgegevens sneller van het ene kanaal naar het andere overbrengen
  • We kunnen een gedeelte van een bestand vergrendelen om de toegang door andere threads te beperken
  • Om gegevensverlies te voorkomen, kunnen we het schrijven van updates naar een bestand onmiddellijk naar de opslag afdwingen

3. Lezen met FileChannel

FileChannel presteert sneller dan standaard I / O wanneer we een groot bestand lezen.

We moeten opmerken dat hoewel een deel van Java NIO, FileChannel operaties blokkeren en hebben geen niet-blokkerende modus.

3.1. Een bestand lezen met FileChannel

Laten we begrijpen hoe we een bestand moeten lezen met FileChannel op een bestand dat het volgende bevat:

Hallo Wereld

Deze test leest het bestand en controleert of het goed is gelezen:

@Test openbare leegte gegevenFile_whenReadWithFileChannelUsingRandomAccessFile_thenCorrect () gooit IOException {try (RandomAccessFile reader = nieuwe RandomAccessFile ("src / test / resources / test_read.in", "r"); FileChannel kanaal = readerOutray.putStream )) {int bufferSize = 1024; if (bufferSize> channel.size ()) {bufferSize = (int) channel.size (); } ByteBuffer buff = ByteBuffer.allocate (bufferSize); while (channel.read (buff)> 0) {out.write (buff.array (), 0, buff.position ()); buff.clear (); } String fileContent = nieuwe String (out.toByteArray (), StandardCharsets.UTF_8); assertEquals ("Hallo wereld", fileContent); }}

Hier lezen we bytes uit het bestand met FileChannel, RandomAccessFile, en ByteBuffer.

Dat moeten we ook opmerken meerdere gelijktijdige threads kunnen gebruiken FileChannels veilig. Er is echter slechts één thread tegelijk toegestaan ​​voor een bewerking waarbij de positie van een kanaal moet worden bijgewerkt of de bestandsgrootte moet worden gewijzigd. Dit blokkeert andere threads die een vergelijkbare bewerking proberen totdat de vorige bewerking is voltooid.

Bewerkingen die expliciete kanaalposities bieden, kunnen echter gelijktijdig worden uitgevoerd zonder te worden geblokkeerd.

3.2. Het openen van een FileChannel

Om een ​​bestand te lezen met FileChannel, we moeten het openen.

Laten we eens kijken hoe we een FileChannel gebruik makend van RandomAccessFile:

RandomAccessFile reader = nieuw RandomAccessFile (bestand, "r"); FileChannel-kanaal = reader.getChannel ();

Modus ‘r 'geeft aan dat het kanaal alleen‘ open voor lezen' is. We moeten opmerken dat het sluiten van een RandomAccessFile zal ook het bijbehorende kanaal sluiten.

Vervolgens zien we het openen van een FileChannel om een ​​bestand te lezen met FileInputStream:

FileInputStream fin = nieuwe FileInputStream (bestand); FileChannel-kanaal = fin.getChannel ();

Evenzo het sluiten van een FileInputStream sluit ook het kanaal dat eraan is gekoppeld.

3.3. Gegevens lezen van een FileChannel

Om de gegevens te lezen, kunnen we een van de lezen methoden.

Laten we eens kijken hoe we een reeks bytes kunnen lezen. We gebruiken een ByteBuffer om de gegevens te bewaren:

ByteBuffer buff = ByteBuffer.allocate (1024); int noOfBytesRead = channel.read (buff); String fileContent = nieuwe String (buff.array (), StandardCharsets.UTF_8); assertEquals ("Hallo wereld", fileContent);

Vervolgens zullen we zien hoe we een reeks bytes kunnen lezen, beginnend bij een bestandspositie:

ByteBuffer buff = ByteBuffer.allocate (1024); int noOfBytesRead = channel.read (buff, 5); String fileContent = nieuwe String (buff.array (), StandardCharsets.UTF_8); assertEquals ("wereld", fileContent);

We moeten er rekening mee houden dat er een Tekenset om een ​​byte-array te decoderen in Draad.

We specificeren de Tekenset waarmee de bytes oorspronkelijk waren gecodeerd. Zonder het, we kunnen eindigen met onleesbare tekst. Met name multi-byte coderingen zoals UTF-8 en UTF-16 is mogelijk niet in staat om een ​​willekeurig gedeelte van het bestand te decoderen, omdat sommige tekens van meerdere bytes onvolledig kunnen zijn.

4. Schrijven met FileChannel

4.1. Schrijven naar een bestand met FileChannel

Laten we eens kijken hoe we kunnen schrijven met FileChannel:

@Test openbare leegte whenWriteWithFileChannelUsingRandomAccessFile_thenCorrect () gooit IOException {String file = "src / test / resources / test_write_using_filechannel.txt"; probeer (RandomAccessFile writer = nieuwe RandomAccessFile (bestand, "rw"); FileChannel-kanaal = writer.getChannel ()) {ByteBuffer buff = ByteBuffer.wrap ("Hallo wereld" .getBytes (StandardCharsets.UTF_8)); channel.write (buff); // controleer RandomAccessFile reader = nieuwe RandomAccessFile (bestand, "r"); assertEquals ("Hallo wereld", reader.readLine ()); reader.close (); }}

4.2. Het openen van een FileChannel

Om in een bestand te schrijven met FileChannel, we moeten het openen.

Laten we eens kijken hoe we een FileChannel gebruik makend van RandomAccessFile:

RandomAccessFile writer = nieuw RandomAccessFile (bestand, "rw"); FileChannel-kanaal = writer.getChannel ();

Modus 'rw' geeft aan dat het kanaal 'open staat voor lezen en schrijven'.

Laten we ook kijken hoe u een FileChannel gebruik makend van FileOutputStream:

FileOutputStream fout = nieuwe FileOutputStream (bestand); FileChannel kanaal = fout.getChannel (); 

4.3. Gegevens schrijven met FileChannel

Om gegevens te schrijven met een FileChannel, kunnen we een van de schrijven methoden.

Laten we eens kijken hoe we een reeks bytes kunnen schrijven met een ByteBuffer om de gegevens op te slaan:

ByteBuffer buff = ByteBuffer.wrap ("Hallo wereld" .getBytes (StandardCharsets.UTF_8)); channel.write (buff); 

Vervolgens zullen we zien hoe u een reeks bytes schrijft, beginnend bij een bestandspositie:

ByteBuffer buff = ByteBuffer.wrap ("Hallo wereld" .getBytes (StandardCharsets.UTF_8)); channel.write (buff, 5); 

5. Huidige positie

FileChannel stelt ons in staat om de positie waarop we lezen of schrijven te krijgen en te veranderen.

Laten we eens kijken hoe we de huidige positie kunnen krijgen:

lang originalPosition = channel.position ();

Laten we vervolgens kijken hoe we de positie kunnen instellen:

kanaal.positie (5); assertEquals (originalPosition + 5, channel.position ());

6. Verkrijg de grootte van een bestand

Laten we eens kijken hoe we de FileChannel.size methode om de grootte van een bestand in bytes te krijgen:

@Test openbare leegte whenGetFileSize_thenCorrect () gooit IOException {RandomAccessFile reader = nieuwe RandomAccessFile ("src / test / resources / test_read.in", "r"); FileChannel-kanaal = reader.getChannel (); // de oorspronkelijke bestandsgrootte is 11 bytes. assertEquals (11, channel.size ()); kanaal.close (); reader.close (); }

7. Een bestand afkappen

Laten we eens kijken hoe we het FileChannel.truncate methode om een ​​bestand af te kappen tot een opgegeven grootte in bytes:

@Test public void whenTruncateFile_thenCorrect () gooit IOException {String input = "dit is een testinvoer"; FileOutputStream fout = nieuwe FileOutputStream ("src / test / resources / test_truncate.txt"); FileChannel kanaal = fout.getChannel (); ByteBuffer buff = ByteBuffer.wrap (input.getBytes ()); channel.write (buff); buff.flip (); channel = channel.truncate (5); assertEquals (5, channel.size ()); fout.close (); kanaal.close (); } 

8. Forceer bestandsupdate in opslag

Een besturingssysteem kan bestandswijzigingen opslaan in het cachegeheugen om prestatieredenen en gegevens kunnen verloren gaan als het systeem crasht. Om de inhoud van bestanden en metadata te dwingen om continu naar schijf te schrijven, kunnen we de dwingen methode:

channel.force (true);

Deze methode is alleen gegarandeerd als het bestand zich op een lokaal apparaat bevindt.

9. Laad een sectie van een bestand in het geheugen

Laten we eens kijken hoe we een sectie van een bestand in het geheugen kunnen laden met FileChannel.map. We gebruiken FileChannel.MapMode.READ_ONLY om het bestand in alleen-lezen modus te openen:

@Test openbare ongeldige gegevenFile_whenReadAFileSectionIntoMemoryWithFileChannel_thenCorrect () gooit IOException {try (RandomAccessFile reader = nieuwe RandomAccessFile ("src / test / resources / test_read.in", "r"); FileChannel channel = reader.utgetSectionArray (); )) {MappedByteBuffer buff = channel.map (FileChannel.MapMode.READ_ONLY, 6, 5); if (buff.hasRemaining ()) {byte [] data = nieuwe byte [buff.remaining ()]; buff.get (gegevens); assertEquals ("wereld", nieuwe String (data, StandardCharsets.UTF_8)); }}}

Evenzo kunnen we gebruiken FileChannel.MapMode.READ_WRITE om het bestand in zowel lees- als schrijfmodus te openen.

We kunnen ook gebruik maken vanFileChannel.MapMode.PRIVATE modus, waarbij de wijzigingen niet van toepassing zijn op het originele bestand.

10. Vergrendel een sectie van een bestand

Laten we eens kijken hoe we een sectie van een bestand kunnen vergrendelen om gelijktijdige toegang tot een sectie te voorkomen met behulp van de FileChannel.tryLock methode:

@Test openbare leegte gegevenFile_whenWriteAFileUsingLockAFileSectionWithFileChannel_thenCorrect () gooit IOException {try (RandomAccessFile reader = nieuwe RandomAccessFile ("src / test / resources / test_read.in", "rw"); FileChannel channel = reader.get = channelLockt. (6, 5, Boolean.FALSE)) {// voer andere bewerkingen uit ... assertNotNull (fileLock); }}

De tryLock methode probeert een vergrendeling op de bestandssectie te krijgen. Als de aangevraagde bestandssectie al is geblokkeerd door een andere thread, wordt een OverlappingFileLockException uitzondering. Deze methode heeft ook een boolean parameter om een ​​gedeeld slot of een exclusief slot aan te vragen.

We moeten er rekening mee houden dat sommige besturingssystemen een gedeelde vergrendeling mogelijk niet toestaan, in plaats daarvan standaard een exclusieve vergrendeling.

11. Afsluiten van een FileChannel

Eindelijk, als we klaar zijn met het gebruik van een FileChannel, we moeten het sluiten. In onze voorbeelden hebben we gebruikt probeer-met-middelen.

Indien nodig kunnen we het FileChannel rechtstreeks met de dichtbij methode:

kanaal.close ();

12. Conclusie

In deze tutorial hebben we gezien hoe te gebruiken FileChannel om bestanden te lezen en te schrijven. Daarnaast hebben we onderzocht hoe we de bestandsgrootte en de huidige lees- / schrijflocatie kunnen lezen en wijzigen, en hebben we gekeken naar het gebruik ervan FileChannels in gelijktijdige of datakritische toepassingen.

Zoals altijd is de broncode voor de voorbeelden beschikbaar op GitHub.


$config[zx-auto] not found$config[zx-overlay] not found