Vodič za Java FileChannel

1. Pregled

U ovom ćemo brzom vodiču pogledati FileChannel klasa predviđena u Java NIO knjižnica. Raspravit ćemo kako čitati i pisati podatke pomoću FileChannel i ByteBuffer.

Također ćemo istražiti prednosti upotrebe FileChannel i neke druge značajke za manipulaciju datotekama.

2. Prednosti FileChannel

Prednosti FileChannel uključuju:

  • Čitanje i pisanje na određenom mjestu u datoteci
  • Učitavanje odjeljka datoteke izravno u memoriju, što može biti učinkovitije
  • Podatkovne datoteke možemo brže prenijeti s jednog kanala na drugi
  • Možemo zaključati odjeljak datoteke kako bismo ograničili pristup drugim nitima
  • Da bismo izbjegli gubitak podataka, možemo prisiliti pisanje ažuriranja u datoteku odmah na pohranu

3. Čitanje s FileChannel

FileChannel izvodi se brže od standardnog I / O kada čitamo veliku datoteku.

Valja napomenuti da iako dio Java NIO, FileChannel operacije blokiraju i nemaju način neblokiranja.

3.1. Čitanje datoteke pomoću FileChannel

Razumijemo kako čitati datoteku pomoću FileChannel na datoteci koja sadrži:

Pozdrav svijete

Ovaj test čita datoteku i provjerava je li pročitana:

@Test public void givenFile_whenReadWithFileChannelUsingRandomAccessFile_thenCorrect () baca IOException {try (RandomAccessFile reader = new RandomAccessFile ("src / test / resources / test_read.in", "r"); FileChannel channel = read.utra )) {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 = novi niz (out.toByteArray (), StandardCharsets.UTF_8); assertEquals ("Pozdrav svijetu", fileContent); }}

Ovdje čitamo bajtove iz datoteke pomoću FileChannel, RandomAccessFile, i ByteBuffer.

To bismo također trebali primijetiti može koristiti više istodobnih niti Kanali datoteka sigurno. Međutim, samo je jednoj niti istovremeno dopuštena operacija koja uključuje ažuriranje položaja kanala ili promjenu veličine datoteke. Ovo blokira druge niti koje pokušavaju sličnu operaciju dok se prethodna operacija ne dovrši.

Međutim, operacije koje pružaju eksplicitne pozicije kanala mogu se istodobno izvoditi bez blokiranja.

3.2. Otvaranje a FileChannel

Da bi se datoteka pročitala pomoću FileChannel, moramo ga otvoriti.

Pogledajmo kako otvoriti a FileChannel koristeći RandomAccessFile:

Čitač RandomAccessFile = novi RandomAccessFile (datoteka, "r"); FileChannel channel = čitač.getChannel ();

Način "r" označava da je kanal otvoren samo za čitanje. Valja napomenuti da zatvaranje a RandomAccessFile također će zatvoriti pridruženi kanal.

Dalje, vidjet ćemo otvaranje a FileChannel za čitanje datoteke pomoću FileInputStream:

FileInputStream fin = novi FileInputStream (datoteka); FileChannel channel = fin.getChannel ();

Slično tome, zatvaranje a FileInputStream također zatvara kanal povezan s njim.

3.3. Čitanje podataka iz a FileChannel

Za čitanje podataka možemo se poslužiti jednim od čitati metode.

Pogledajmo kako čitati slijed bajtova. Koristit ćemo a ByteBuffer za čuvanje podataka:

ByteBuffer buff = ByteBuffer.allocate (1024); int noOfBytesRead = channel.read (buff); String fileContent = novi niz (buff.array (), StandardCharsets.UTF_8); assertEquals ("Pozdrav svijetu", fileContent);

Dalje, vidjet ćemo kako čitati slijed bajtova, počevši od položaja datoteke:

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

Trebali bismo primijetiti potrebu za a Charset za dekodiranje bajt polja u Niz.

Određujemo Charset s kojim su izvorno kodirani bajtovi. Bez toga, možda ćemo završiti s iskrivljenim tekstom. Konkretno, višebajtna kodiranja poput UTF-8 i UTF-16 možda neće moći dekodirati proizvoljan odjeljak datoteke jer neki višebajtni znakovi mogu biti nepotpuni.

4. Pisanje sa FileChannel

4.1. Zapisivanje u datoteku pomoću FileChannel

Istražimo kako pisati koristeći FileChannel:

@Test public void whenWriteWithFileChannelUsingRandomAccessFile_thenCorrect () baca IOException {String file = "src / test / resources / test_write_using_filechannel.txt"; probajte (RandomAccessFile Writer = novi RandomAccessFile (datoteka, "rw"); FileChannel channel = writer.getChannel ()) {ByteBuffer buff = ByteBuffer.wrap ("Hello world" .getBytes (StandardCharsets.UTF_8)); channel.write (buff); // provjera RandomAccessFile čitač = novi RandomAccessFile (datoteka, "r"); assertEquals ("Pozdrav svijetu", reader.readLine ()); čitač.close (); }}

4.2. Otvaranje a FileChannel

Da biste zapisali u datoteku pomoću FileChannel, moramo ga otvoriti.

Pogledajmo kako otvoriti a FileChannel koristeći RandomAccessFile:

RandomAccessFile Writer = novi RandomAccessFile (datoteka, "rw"); FileChannel channel = Writer.getChannel ();

Način "rw" označava da je kanal "otvoren za čitanje i pisanje".

Pogledajmo i kako otvoriti FileChannel koristeći FileOutputStream:

FileOutputStream fout = novi FileOutputStream (datoteka); FileChannel channel = fout.getChannel (); 

4.3. Zapisivanje podataka pomoću FileChannel

Za pisanje podataka pomoću a FileChannel, možemo koristiti jedan od pisati metode.

Pogledajmo kako napisati slijed bajtova pomoću a ByteBuffer za pohranu podataka:

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

Dalje, vidjet ćemo kako napisati slijed bajtova, počevši od položaja datoteke:

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

5. Trenutni položaj

FileChannel omogućuje nam da dobijemo i promijenimo položaj u kojem čitamo ili pišemo.

Pogledajmo kako dobiti trenutnu poziciju:

duga originalPosition = channel.position ();

Dalje, pogledajmo kako postaviti položaj:

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

6. Dohvatite veličinu datoteke

Pogledajmo kako koristiti FileChannel.size metoda za dobivanje veličine datoteke u bajtovima:

@Test public void whenGetFileSize_thenCorrect () baca IOException {RandomAccessFile reader = new RandomAccessFile ("src / test / resources / test_read.in", "r"); FileChannel channel = čitač.getChannel (); // izvorna veličina datoteke je 11 bajtova. assertEquals (11, channel.size ()); channel.close (); čitač.close (); }

7. Skratite datoteku

Razumijemo kako koristiti FileChannel.truncate metoda za skraćivanje datoteke na zadanu veličinu u bajtovima:

@Test public void whenTruncateFile_thenCorrect () baca IOException {String input = "ovo je testni ulaz"; FileOutputStream fout = novi FileOutputStream ("src / test / resources / test_truncate.txt"); FileChannel channel = fout.getChannel (); ByteBuffer buff = ByteBuffer.wrap (input.getBytes ()); channel.write (buff); buff.flip (); channel = channel.truncate (5); assertEquals (5, channel.size ()); fout.close (); channel.close (); } 

8. Prisilno ažuriranje datoteke u pohranu

Operativni sustav može predmemorirati promjene datoteka iz razloga performansi, a podaci se mogu izgubiti ako se sustav sruši. Da bismo prisilili sadržaj datoteke i metapodatke da neprestano zapisuju na disk, možemo koristiti sila metoda:

channel.force (true);

Ova metoda zajamčena je samo kada se datoteka nalazi na lokalnom uređaju.

9. Učitajte odjeljak datoteke u memoriju

Pogledajmo kako pomoću datoteke učitati odjeljak datoteke u memoriju FileChannel.map. Koristimo FileChannel.MapMode.READ_ONLY za otvaranje datoteke u načinu samo za čitanje:

@Test public void givenFile_whenReadAFileSectionIntoMemoryWithFileChannel_thenCorrect () baca IOException {try (RandomAccessFile reader = new RandomAccessFile ("src / test / resources / test_read.in", "r"); ByChannel channel = čitač. )) {MappedByteBuffer buff = channel.map (FileChannel.MapMode.READ_ONLY, 6, 5); if (buff.hasRemaining ()) {byte [] data = novi bajt [buff.remaining ()]; buff.get (podaci); assertEquals ("svijet", novi String (podaci, StandardCharsets.UTF_8)); }}}

Slično tome, možemo koristiti FileChannel.MapMode.READ_WRITE za otvaranje datoteke u načinu čitanja i pisanja.

Možemo i koristitiFileChannel.MapMode.PRIVATE način, gdje se promjene ne odnose na izvornu datoteku.

10. Zaključajte odjeljak datoteke

Razumijemo kako zaključati odjeljak datoteke kako bismo spriječili istodobni pristup odjeljku pomoću FileChannel.tryLock metoda:

@Test public void givenFile_whenWriteAFileUsingLockAFileSectionWithFileChannel_thenCorrect () baca IOException {try (RandomAccessFile čitač = novi RandomAccessFile ("src / test / resources / test_read.in", "file.thannel.ckLockLanLockLanLockLankLackLockLanLockLanLockLanLockLanLockLanLockLanLockLanLockLackLockLanLockLackLockLackLockLanLockLackLockLanLockLackLockLanColnel (6, 5, Boolean.FALSE)) {// radite druge operacije ... assertNotNull (fileLock); }}

The tryLock metoda pokušava zaključati odjeljak datoteke. Ako je odjeljak tražene datoteke već blokiran drugom niti, on baca datoteku OverlappingFileLockException iznimka. Ova metoda također uzima a boolean parametar da zatražite ili zajedničku ili isključivu bravu.

Trebali bismo imati na umu da neki operativni sustavi možda ne dopuštaju zajedničko zaključavanje, već se prema zadanim postavkama postavlja ekskluzivno zaključavanje.

11. Zatvaranje a FileChannel

Napokon, kada završimo s upotrebom a FileChannel, moramo ga zatvoriti. U našim primjerima koje smo koristili pokušajte s resursima.

Ako je potrebno, možemo zatvoriti FileChannel izravno s Zatvoriti metoda:

channel.close ();

12. Zaključak

U ovom uputstvu smo vidjeli kako koristiti FileChannel za čitanje i pisanje datoteka. Osim toga, istražili smo kako čitati i mijenjati veličinu datoteke i njezino trenutno mjesto za čitanje / pisanje i pogledali kako se koristi Kanali datoteka u istodobnim aplikacijama ili podacima kritičnim.

Kao i uvijek, izvorni kod za primjere dostupan je na GitHubu.