Vejledning til Java FileChannel

1. Oversigt

I denne hurtige vejledning ser vi på FileChannel klasse i Java NIO bibliotek. Vi diskuterer hvordan man læser og skriver data ved hjælp af FileChannel og ByteBuffer.

Vi undersøger også fordelene ved at bruge FileChannel og nogle af dens andre filmanipulationsfunktioner.

2. Fordele ved FileChannel

Fordelene ved FileChannel omfatte:

  • Læsning og skrivning på en bestemt position i en fil
  • Indlæsning af en sektion af en fil direkte i hukommelsen, som kan være mere effektiv
  • Vi kan overføre fildata fra en kanal til en anden i en hurtigere hastighed
  • Vi kan låse en del af en fil for at begrænse adgangen til andre tråde
  • For at undgå tab af data kan vi tvinge skrivning af opdateringer til en fil til lagring med det samme

3. Læsning med FileChannel

FileChannel fungerer hurtigere end standard I / O, når vi læser en stor fil.

Vi skal bemærke, at selvom en del af Java NIO, FileChannel operationer blokerer og har ikke en ikke-blokerende tilstand.

3.1. Læsning af en fil ved hjælp af FileChannel

Lad os forstå, hvordan man læser en fil ved hjælp af FileChannel på en fil, der indeholder:

Hej Verden

Denne test læser filen og kontrollerer, at den er læst ok:

@Test offentlig ugyldighed givenFile_whenReadWithFileChannelUsingRandomAccessFile_thenCorrect () kaster IOException {try (RandomAccessFile reader = new RandomAccessFile ("src / test / resources / test_read.in", "r"); FileChannel channel = reader.getStream = )) {int bufferSize = 1024; hvis (bufferSize> channel.size ()) {bufferSize = (int) channel.size (); } ByteBuffer buff = ByteBuffer.allocate (bufferSize); mens (channel.read (buff)> 0) {out.write (buff.array (), 0, buff.position ()); buff.clear (); } Streng fileContent = ny streng (out.toByteArray (), StandardCharsets.UTF_8); assertEquals ("Hej verden", fileContent); }}

Her læser vi bytes fra filen ved hjælp af FileChannel, RandomAccessFileog ByteBuffer.

Vi skal også bemærke det flere samtidige tråde kan bruge FileChannels sikkert. Imidlertid er kun én tråd ad gangen tilladt for en operation, der involverer opdatering af en kanals position eller ændring af filstørrelsen. Dette blokerer andre tråde, der forsøger en lignende operation, indtil den foregående operation er afsluttet.

Handlinger, der giver eksplicitte kanalpositioner, kan dog køre samtidigt uden at blive blokeret.

3.2. Åbning af en FileChannel

For at læse en fil ved hjælp af FileChannel, vi skal åbne den.

Lad os se, hvordan man åbner en FileChannel ved brug af RandomAccessFile:

RandomAccessFile-læser = ny RandomAccessFile (fil, "r"); FileChannel channel = reader.getChannel ();

Mode 'r' angiver, at kanalen kun er 'åben til læsning'. Vi skal bemærke, at lukning af en RandomAccessFile lukker også den tilknyttede kanal.

Derefter ser vi åbning af en FileChannel at læse en fil ved hjælp af FileInputStream:

FileInputStream fin = ny FileInputStream (fil); FileChannel kanal = fin.getChannel ();

Tilsvarende lukker en FileInputStream lukker også den kanal, der er knyttet til den.

3.3. Læsning af data fra en FileChannel

For at læse dataene kan vi bruge en af Læs metoder.

Lad os se, hvordan man læser en række bytes. Vi bruger en ByteBuffer at gemme dataene:

ByteBuffer buff = ByteBuffer.allocate (1024); int noOfBytesRead = channel.read (buff); String fileContent = ny streng (buff.array (), StandardCharsets.UTF_8); assertEquals ("Hej verden", fileContent);

Derefter ser vi, hvordan man læser en række bytes, startende ved en filposition:

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

Vi skal bemærke behovet for en Charset at afkode et byte-array i Snor.

Vi specificerer Charset som bytes oprindeligt blev kodet med. Uden det, vi ender muligvis med forvrænget tekst. Især multi-byte-kodninger som UTF-8 og UTF-16 muligvis ikke i stand til at afkode et vilkårligt afsnit af filen, da nogle af multi-byte-tegn muligvis er ufuldstændige.

4. Skrivning med FileChannel

4.1. Skrivning til en fil ved hjælp af FileChannel

Lad os undersøge, hvordan man skriver ved hjælp af FileChannel:

@Test offentlig ugyldig nårWriteWithFileChannelUsingRandomAccessFile_thenCorrect () kaster IOException {String file = "src / test / resources / test_write_using_filechannel.txt"; prøv (RandomAccessFile forfatter = ny RandomAccessFile (fil, "rw"); FileChannel kanal = writer.getChannel ()) {ByteBuffer buff = ByteBuffer.wrap ("Hello world" .getBytes (StandardCharsets.UTF_8)); channel.write (buff); // verificer RandomAccessFile-læser = ny RandomAccessFile (fil, "r"); assertEquals ("Hej verden", reader.readLine ()); reader.close (); }}

4.2. Åbning af en FileChannel

For at skrive ind i en fil ved hjælp af FileChannel, vi skal åbne den.

Lad os se, hvordan man åbner en FileChannel ved brug af RandomAccessFile:

RandomAccessFile forfatter = ny RandomAccessFile (fil, "rw"); FileChannel channel = writer.getChannel ();

Tilstand 'rw' angiver, at kanalen er 'åben for læsning og skrivning'.

Lad os også se, hvordan man åbner en FileChannel ved brug af FileOutputStream:

FileOutputStream fout = ny FileOutputStream (fil); FileChannel kanal = fout.getChannel (); 

4.3. Skrivning af data med FileChannel

At skrive data med en FileChannel, kan vi bruge en af skrive metoder.

Lad os se hvordan man skriver en sekvens af bytes ved hjælp af a ByteBuffer at gemme dataene:

ByteBuffer buff = ByteBuffer.wrap ("Hello world" .getBytes (StandardCharsets.UTF_8)); channel.write (buff); 

Derefter ser vi, hvordan vi skriver en sekvens af bytes, der starter ved en filposition:

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

5. Nuværende position

FileChannel giver os mulighed for at få og ændre den position, hvor vi læser eller skriver.

Lad os se, hvordan man får den aktuelle position:

lang originalPosition = channel.position ();

Lad os derefter se, hvordan du indstiller positionen:

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

6. Få størrelsen på en fil

Lad os se, hvordan du bruger FileChannel.size metode til at få størrelsen på en fil i byte:

@Test offentlig ugyldig nårGetFileSize_thenCorrect () kaster IOException {RandomAccessFile læser = ny RandomAccessFile ("src / test / resources / test_read.in", "r"); FileChannel channel = reader.getChannel (); // den originale filstørrelse er 11 byte. assertEquals (11, channel.size ()); channel.close (); reader.close (); }

7. Afkort en fil

Lad os forstå, hvordan man bruger FileChannel.trunker metode til at afkorte en fil til den givne størrelse i byte:

@Test offentlig ugyldig nårTruncateFile_thenCorrect () kaster IOException {String input = "dette er en testinput"; FileOutputStream fout = ny FileOutputStream ("src / test / resources / test_truncate.txt"); FileChannel kanal = fout.getChannel (); ByteBuffer buff = ByteBuffer.wrap (input.getBytes ()); channel.write (buff); buff.flip (); kanal = channel.truncate (5); assertEquals (5, channel.size ()); fout.close (); channel.close (); } 

8. Tving filopdatering til lagring

Et operativsystem kan cache filændringer af ydeevneårsager, og data kan gå tabt, hvis systemet går ned. For at tvinge filindhold og metadata til at skrive til disken kontinuerligt kan vi bruge kraft metode:

channel.force (sand);

Denne metode garanteres kun, når filen findes på en lokal enhed.

9. Læg en del af en fil i hukommelsen

Lad os se, hvordan man indlæser et afsnit af en fil i hukommelsen ved hjælp af FileChannel.map. Vi bruger FileChannel.MapMode.READ_ONLY for at åbne filen i skrivebeskyttet tilstand:

@Test offentlig ugyldighed givenFile_whenReadAFileSectionIntoMemoryWithFileChannel_thenCorrect () kaster IOException {prøv (RandomAccessFile læser = ny RandomAccessFile ("src / test / resources / test_read.in", "r"); FileChannel channel = reader.getStreamray)) )) {MappedByteBuffer buff = channel.map (FileChannel.MapMode.READ_ONLY, 6, 5); hvis (buff.hasRemaining ()) {byte [] data = ny byte [buff.remaining ()]; buff.get (data); assertEquals ("verden", ny streng (data, StandardCharsets.UTF_8)); }}}

På samme måde kan vi bruge FileChannel.MapMode.READ_WRITE for at åbne filen i både læse- og skrivetilstand.

Vi kan også brugeFileChannel.MapMode.PRIVATE tilstand, hvor ændringer ikke gælder for den originale fil.

10. Lås en del af en fil

Lad os forstå, hvordan man låser en sektion af en fil for at forhindre samtidig adgang til en sektion ved hjælp af FileChannel.tryLock metode:

@Test offentlig ugyldighed givenFile_whenWriteAFileUsingLockAFileSectionWithFileChannel_thenCorrect () kaster IOException {try (RandomAccessFile reader = new RandomAccessFile ("src / test / resources / test_read.in", "rw"); FileChannel channel = reader.get; (6, 5, Boolean.FALSE)) {// gør andre operationer ... assertNotNull (fileLock); }}

Det tryLock metode forsøger at skaffe en lås på filafsnittet. Hvis den anmodede filsektion allerede er blokeret af en anden tråd, kaster den en OverlappingFileLockException undtagelse. Denne metode tager også en boolsk parameter for at anmode om en delt lås eller en eksklusiv lås.

Vi skal bemærke, at nogle operativsystemer muligvis ikke tillader en delt lås, men som standard er en eksklusiv lås.

11. Afslutning a FileChannel

Endelig når vi er færdige med at bruge en FileChannel, vi skal lukke det. I vores eksempler har vi brugt prøv med ressourcer.

Hvis det er nødvendigt, kan vi lukke FileChannel direkte med tæt metode:

channel.close ();

12. Konklusion

I denne vejledning har vi set hvordan man bruger FileChannel at læse og skrive filer. Derudover har vi undersøgt, hvordan man læser og ændrer filstørrelsen og dens aktuelle læse / skrive placering og har set på, hvordan man bruger FileChannels i samtidige eller datakritiske applikationer.

Som altid er kildekoden til eksemplerne tilgængelig på GitHub.