Stiahnite si súbor z adresy URL v prostredí Java

1. Úvod

V tejto príručke uvidíme niekoľko metód, ktoré môžeme použiť na stiahnutie súboru.

Preberieme si príklady od základného použitia Java IO po balíček NIO a niektoré bežné knižnice ako Async Http Client a Apache Commons IO.

Na záver si povieme, ako môžeme obnoviť sťahovanie, ak naše pripojenie zlyhá skôr, ako sa načíta celý súbor.

2. Používanie Java IO

Najzákladnejším API, ktoré môžeme použiť na stiahnutie súboru, je Java IO. Môžeme použiť URL triedy na otvorenie pripojenia k súboru, ktorý si chceme stiahnuť. Na efektívne prečítanie súboru použijeme openStream () metóda na získanie InputStream:

BufferedInputStream in = nový BufferedInputStream (nová adresa URL (FILE_URL) .opStStream ())

Pri čítaní z InputStream, odporúča sa zabaliť do a BufferedInputStream na zvýšenie výkonu.

Zvýšenie výkonu pochádza z ukladania do vyrovnávacej pamäte. Pri čítaní po jednom bajte pomocou čítať() metóda, každé volanie metódy implikuje systémové volanie do základného súborového systému. Keď JVM vyvolá čítať() systémové volanie, kontext vykonania programu sa prepne z užívateľského režimu do režimu jadra a späť.

Tento kontextový prepínač je z hľadiska výkonu drahý. Keď prečítame veľký počet bajtov, bude výkon aplikácie slabý z dôvodu veľkého počtu použitých kontextových prepínačov.

Na zápis bajtov načítaných z adresy URL do nášho lokálneho súboru použijeme znak napíš () metóda z FileOutputStream trieda:

try (BufferedInputStream in = new BufferedInputStream (new URL (FILE_URL) .openStream ()); FileOutputStream fileOutputStream = new FileOutputStream (FILE_NAME)) {byte dataBuffer [] = new byte [1024]; int bytesRead; while ((bytesRead = in.read (dataBuffer, 0, 1024))! = -1) {fileOutputStream.write (dataBuffer, 0, bytesRead); }} catch (IOException e) {// handle exception}

Pri použití a BufferedInputStream, the čítať() metóda prečíta toľko bajtov, koľko sme nastavili pre veľkosť medzipamäte. V našom príklade to už robíme tak, že čítame bloky 1024 bajtov naraz, takže BufferedInputStream nie je potrebné.

Vyššie uvedený príklad je veľmi podrobný, ale našťastie od verzie Java 7 máme Súbory trieda, ktorá obsahuje pomocné metódy pre manipuláciu s operáciami IO. Môžeme použiť Files.copy () metóda na čítanie všetkých bajtov z InputStream a skopírujte ich do lokálneho súboru:

InputStream in = nová URL (FILE_URL) .opStStream (); Files.copy (v, Paths.get (FILE_NAME), StandardCopyOption.REPLACE_EXISTING);

Náš kód funguje dobre, ale je možné ho vylepšiť. Jeho hlavnou nevýhodou je skutočnosť, že bajty sa ukladajú do pamäte.

Našťastie nám Java ponúka balíček NIO, ktorý obsahuje metódy na prenos bajtov priamo medzi 2 Kanály bez medzipamäte.

V ďalšej časti sa pozrieme na podrobnosti.

3. Používanie NIO

Balík Java NIO ponúka možnosť prenášať bajty medzi 2 Kanály bez ich ukladania do pamäte aplikácie.

Ak chcete súbor prečítať z našej adresy URL, vytvoríme novú ReadableByteChannel z URL Prúd:

ReadableByteChannel readableByteChannel = Channels.newChannel (url.openStream ());

Bajty načítané z ReadableByteChannel bude prevedený do a FileChannel zodpovedajúci súboru, ktorý sa stiahne:

FileOutputStream fileOutputStream = nový FileOutputStream (FILE_NAME); FileChannel fileChannel = fileOutputStream.getChannel ();

Použijeme transferFrom () metóda z ReadableByteChannel triedy na stiahnutie bajtov z danej adresy URL do našej FileChannel:

fileOutputStream.getChannel () .transferFrom (readableByteChannel, 0, Long.MAX_VALUE);

The transferTo () a transferFrom () metódy sú efektívnejšie ako jednoduché čítanie z toku pomocou medzipamäte. V závislosti od základného operačného systému údaje je možné prenášať priamo z medzipamäte súborového systému do nášho súboru bez kopírovania akýchkoľvek bajtov do pamäte aplikácie.

V systémoch Linux a UNIX tieto metódy používajú nulová kópia technika, ktorá znižuje počet kontextových prepínaní medzi režimom jadra a režimom používateľa.

4. Používanie knižníc

Na príkladoch vyššie sme videli, ako môžeme sťahovať obsah z adresy URL iba pomocou základnej funkcie Java. Môžeme tiež využiť funkcie existujúcich knižníc na uľahčenie našej práce, keď nie sú potrebné vylepšenia výkonu.

Napríklad v scenári zo skutočného sveta by sme potrebovali, aby bol náš kód na stiahnutie asynchrónny.

Mohli by sme zabaliť všetku logiku do a Vyvolávateľná, alebo by sme na to mohli použiť existujúcu knižnicu.

4.1. Asynchronizovaný klient HTTP

AsyncHttpClient je populárna knižnica na vykonávanie asynchrónnych požiadaviek HTTP pomocou rámca Netty. Môžeme ho použiť na vykonanie požiadavky GET na adresu URL súboru a na získanie obsahu súboru.

Najskôr musíme vytvoriť klienta HTTP:

AsyncHttpClient client = Dsl.asyncHttpClient ();

Stiahnutý obsah bude umiestnený do a FileOutputStream:

Stream FileOutputStream = nový FileOutputStream (FILE_NAME);

Ďalej vytvoríme požiadavku HTTP GET a zaregistrujeme AsyncCompletionHandler obslužný program na spracovanie stiahnutého obsahu:

client.prepareGet (FILE_URL) .execute (new AsyncCompletionHandler () {@Override public State onBodyPartReceived (HttpResponseBodyPart bodyPart) vyvolá výnimku {stream.getChannel (). write (bodyPart.getBodyByteBuffer.C)) FileOutputStream onCompleted (odpoveď odozvy) vyvolá výnimku {spätný prúd;}})

Všimnite si, že sme prepísali onBodyPartReceived () metóda. Predvolená implementácia akumuluje prijaté HTTP bloky do ArrayList. To by mohlo viesť k vysokej spotrebe pamäte alebo Nedostatok pamäte výnimkou pri pokuse o stiahnutie veľkého súboru.

Namiesto toho, aby sa každý hromadil HttpResponseBodyPart do pamäti, používame a FileChannel priamo zapísať bajty do nášho lokálneho súboru. Použijeme getBodyByteBuffer () metóda prístupu k obsahu časti tela prostredníctvom a ByteBuffer.

ByteBuffers majú tú výhodu, že pamäť je pridelená mimo haldy JVM, takže to neovplyvní pamäť aplikácií.

4.2. Apache Commons IO

Ďalšou veľmi používanou knižnicou na prevádzku IO je Apache Commons IO. Z Javadoc vidíme, že existuje pomenovaná trieda nástrojov FileUtils ktorý sa používa na bežné úlohy manipulácie so súbormi.

Na stiahnutie súboru z adresy URL môžeme použiť tento jednorázový riadok:

FileUtils.copyURLToFile (nová URL (FILE_URL), nový súbor (FILE_NAME), CONNECT_TIMEOUT, READ_TIMEOUT);

Z hľadiska výkonu je tento kód rovnaký ako ten, ktorý sme uviedli v časti 2.

Podkladový kód používa rovnaké koncepty čítania niektorých bajtov zo slučky v cykle InputStream a písať ich do OutputStream.

Jedným z rozdielov je skutočnosť, že tu URLConnection trieda sa používa na riadenie časových limitov pripojenia, aby sa sťahovanie neblokovalo na dlhšiu dobu:

Pripojenie URLConnection = source.openConnection (); connection.setConnectTimeout (connectionTimeout); connection.setReadTimeout (readTimeout);

5. Obnoviteľné sťahovanie

Vzhľadom na to, že internetové pripojenie z času na čas zlyhá, je užitočné, aby sme mohli pokračovať v sťahovaní namiesto toho, aby sme súbor sťahovali znova od nulového bajtu.

Pre doplnenie tejto funkcie prepíšeme prvý príklad z predchádzajúcich.

Prvá vec, ktorú by sme mali vedieť, je môžeme načítať veľkosť súboru z danej adresy URL bez toho, aby sme ho skutočne stiahli pomocou metódy HTTP HEAD:

URL URL = nová URL (FILE_URL); HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection (); httpConnection.setRequestMethod ("HLAVA"); long removeFileSize = httpConnection.getContentLengthLong ();

Teraz, keď máme celkovú veľkosť obsahu súboru, môžeme skontrolovať, či je náš súbor čiastočne stiahnutý. Ak je to tak, obnovíme sťahovanie od posledného bajtu zaznamenaného na disku:

long existingFileSize = outputFile.length (); if (existingFileSize <fileLength) {httpFileConnection.setRequestProperty ("Range", "bytes =" + existingFileSize + "-" + fileLength); }

Čo sa tu stane, je to nakonfigurovali sme URLConnection požiadať o bajty súborov v konkrétnom rozsahu. Rozsah začína od posledného stiahnutého bajtu a končí sa bajtom zodpovedajúcim veľkosti vzdialeného súboru.

Ďalším bežným spôsobom použitia Rozsah header slúži na stiahnutie súboru v blokoch nastavením rôznych rozsahov bajtov. Napríklad na stiahnutie súboru 2 kB môžeme použiť rozsah 0 - 1024 a 1024 - 2048.

Ďalším jemným rozdielom od kódu v časti 2. je, že FileOutputStream sa otvára pomocou pridať parameter nastavený na hodnotu true:

OutputStream os = nový FileOutputStream (FILE_NAME, true);

Po vykonaní tejto zmeny je zvyšok kódu identický s tým, ktorý sme videli v časti 2.

6. Záver

V tomto článku sme videli niekoľko spôsobov, ako môžeme stiahnuť súbor z adresy URL v prostredí Java.

Najbežnejšia implementácia je implementácia, v ktorej vyrovnávame bajty pri vykonávaní operácií čítania a zápisu. Túto implementáciu je bezpečné použiť aj pre veľké súbory, pretože do pamäte nenačítame celý súbor.

Tiež sme videli, ako môžeme implementovať sťahovanie s nulovou kópiou pomocou Java NIO Kanály. Je to užitočné, pretože to minimalizovalo počet kontextových prepínačov vykonaných pri čítaní a zápise bajtov a pomocou priamych vyrovnávacích pamätí sa bajty nenačítajú do pamäte aplikácie.

Pretože sa sťahovanie súborov zvyčajne vykonáva prostredníctvom protokolu HTTP, ukázali sme si, ako to môžeme dosiahnuť pomocou knižnice AsyncHttpClient.

Zdrojový kód článku je k dispozícii na GitHub.


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