Fájl letöltése Java-beli URL-ről
1. Bemutatkozás
Ebben az oktatóanyagban számos módszert fogunk látni, amelyekkel fájlokat tölthetünk le.
Bemutatunk példákat a Java IO alapvető használatától a NIO csomagig, és néhány általános könyvtárat, például az Async Http klienst és az Apache Commons IO-t.
Végül beszélünk arról, hogyan folytathatjuk a letöltést, ha a kapcsolat meghiúsul, mielőtt a teljes fájlt elolvassák.
2. Java IO használata
A legalapvetőbb API, amelyet egy fájl letöltésére használhatunk, a Java IO. Használhatjuk a URL osztály megnyitja a kapcsolatot a letölteni kívánt fájlhoz. A fájl hatékony olvasásához a openStream () módszer egy InputStream:
BufferedInputStream in = új BufferedInputStream (új URL (FILE_URL) .openStream ())
Amikor olvas egy InputStream, ajánlott becsomagolni a BufferedInputStream a teljesítmény növelése érdekében.
A teljesítménynövekedés a pufferelésből származik. Amikor egy bájtot olvas be egyszerre a olvas() metódus, minden metódushívás egy rendszerhívást jelent az alapul szolgáló fájlrendszer felé. Amikor a JVM a olvas() rendszerhívás esetén a program végrehajtási kontextusa felhasználói módról kernelmódra és vissza kapcsol.
Ez a kontextuskapcsoló teljesítmény szempontból drága. Amikor nagyszámú bájtot olvasunk, az alkalmazás teljesítménye gyenge lesz, a nagyszámú összefüggésváltó miatt.
Az URL-ről beolvasott bájtok helyi fájlunkba történő írásához a ír() módszer a FileOutputStream osztály:
próbáld meg (BufferedInputStream in = új BufferedInputStream (új URL (FILE_URL) .openStream ()); FileOutputStream fileOutputStream = új FileOutputStream (FILE_NAME)) {byte dataBuffer [] = új byte [1024]; int bytesRead; while ((bytesRead = in.read (dataBuffer, 0, 1024))! = -1) {fileOutputStream.write (dataBuffer, 0, bytesRead); }} catch (IOException e) {// kivétel kezelése}
Ha a BufferedInputStream, a olvas() A metódus annyi bájtot fog olvasni, amennyit beállítottunk a puffer méretéhez. Példánkban ezt már úgy csináljuk, hogy egyszerre 1024 bájtos blokkokat olvasunk el, tehát BufferedInputStream nem szükséges.
A fenti példa nagyon bőbeszédű, de szerencsére a Java 7-től kezdve megvan a Fájlok osztály, amely segítő módszereket tartalmaz az IO műveletek kezelésére. Használhatjuk a Files.copy () módszer az összes bájt leolvasására egy InputStream és másolja át őket egy helyi fájlba:
InputStream in = új URL (FILE_URL) .openStream (); Files.copy (itt: Paths.get (FILE_NAME), StandardCopyOption.REPLACE_EXISTING);
Kódunk jól működik, de javítható. Legfőbb hátránya, hogy a bájtokat a memóriába pufferolják.
Szerencsére a Java felajánlja nekünk azt a NIO csomagot, amelynek módszerei vannak a bájtok közvetlen átvitelére 2 között Csatornák pufferelés nélkül.
A következő szakaszban részletezzük.
3. A NIO használata
A Java NIO csomag lehetőséget kínál bájtok átvitelére 2 között Csatornák pufferelés nélkül az alkalmazás memóriájába.
Ahhoz, hogy elolvassa a fájlt az URL-jünkről, létrehozunk egy újat ReadableByteChannel tól URL folyam:
ReadableByteChannel readableByteChannel = Channels.newChannel (url.openStream ());
A bájtok a ReadableByteChannel átkerül a FileChannel a letöltendő fájlnak megfelelő:
FileOutputStream fileOutputStream = új FileOutputStream (FILE_NAME); FileChannel fileChannel = fileOutputStream.getChannel ();
Használjuk a transferFrom () módszer a ReadableByteChannel osztályban töltse le a bájtokat a megadott URL-ről a mi weboldalunkra FileChannel:
fileOutputStream.getChannel () .transferFrom (readableByteChannel, 0, Long.MAX_VALUE);
A transferTo () és transferFrom () a módszerek hatékonyabbak, mint egyszerűen egy adatfolyamból puffer segítségével olvasni. Az alapul szolgáló operációs rendszertől függően az adatok a fájlrendszer gyorsítótárából közvetlenül átkerülhetnek a fájlunkba anélkül, hogy bájtokat másolnának az alkalmazás memóriájába.
Linux és UNIX rendszereken ezek a módszerek a nulla példány technika, amely csökkenti a rendszermag mód és a felhasználói mód közötti kapcsolatok számát.
4. Könyvtárak használata
A fenti példákban láthattuk, hogyan tölthetünk le tartalmat egy URL-ről, csupán a Java alapvető funkcióinak használatával. Kihasználhatjuk a meglévő könyvtárak funkcionalitását is a munkánk megkönnyítése érdekében, amikor a teljesítményre nincs szükség.
Például valós helyzetben szükségünk lenne arra, hogy a letöltési kódunk aszinkron legyen.
Az egész logikát a-ba csomagolhatnánk Hívható, vagy használhatnánk ehhez egy meglévő könyvtárat.
4.1. Async HTTP kliens
Az AsyncHttpClient egy népszerű könyvtár aszinkron HTTP kérések futtatásához a Netty keretrendszer segítségével. Használhatjuk a fájl URL-jére vonatkozó GET kérés végrehajtására és a fájl tartalmának megszerzésére.
Először létre kell hoznunk egy HTTP klienst:
AsyncHttpClient kliens = Dsl.asyncHttpClient ();
A letöltött tartalmat a FileOutputStream:
FileOutputStream stream = új FileOutputStream (FILE_NAME);
Ezután létrehozunk egy HTTP GET kérést és regisztrálunk egy AsyncCompletionHandler kezelő a letöltött tartalom feldolgozásához:
client.prepareGet (FILE_URL) .execute (new AsyncCompletionHandler () {@Orride public state onBodyPartReceived (HttpResponseBodyPart bodyPart) dobja a Kivételt: {stream.getChannel (). A FileOutputStream onCompleted (válaszválasz) kiveti a {visszatérés;}} kivételt)
Figyelje meg, hogy felülbíráltuk a onBodyPartReceived () módszer. Az alapértelmezett megvalósítás a kapott HTTP-darabokat egy Tömb lista. Ez magas memóriafelhasználást vagy an Elfogyott a memória kivétel egy nagy fájl letöltésekor.
Ahelyett, hogy mindegyiket felhalmoznák HttpResponseBodyPart az emlékezetbe, használjuk a FileChannel hogy a bájtokat közvetlenül a helyi fájlunkba írjuk. Használjuk a getBodyByteBuffer () módszer a testrész tartalmának a ByteBuffer.
ByteBuffers előnye, hogy a memória a JVM halomon kívül van lefoglalva, így ez nem befolyásolja az alkalmazások memóriáját.
4.2. Apache Commons IO
Az IO működéséhez egy másik nagyon használt könyvtár az Apache Commons IO. A Javadoc-ból láthatjuk, hogy van egy segédprogram-osztály FileUtils amelyet általános fájlkezelési feladatokhoz használnak.
Fájl letöltéséhez egy URL-ből ezt az egyvonalas vonalat használhatjuk:
FileUtils.copyURLToFile (új URL (FILE_URL), új File (FILE_NAME), CONNECT_TIMEOUT, READ_TIMEOUT);
Teljesítmény szempontjából ez a kód megegyezik azzal, amelyet a 2. szakaszban bemutattunk.
Az alapul szolgáló kód ugyanazokat a fogalmakat használja, hogy egy ciklusban olvasson néhány bájtot egy InputStream és megírja őket egy OutputStream.
Az egyik különbség az a tény, hogy itt a URLConnection osztály a kapcsolat időkorlátjainak vezérlésére szolgál, hogy a letöltés ne akadályozzon nagy ideig:
URLConnection kapcsolat = source.openConnection (); connection.setConnectTimeout (connectionTimeout); connection.setReadTimeout (readTimeout);
5. Folytatható letöltés
Figyelembe véve az internetkapcsolatok időnkénti meghibásodását, hasznos, hogy folytathassuk a letöltést, ahelyett, hogy a fájlt újra letöltenénk a nulla bájtról.
Írjuk át a korábbi első példát ennek a funkciónak a hozzáadásához.
Az első dolog, amit tudnunk kell, az kiolvashatjuk a fájl méretét egy adott URL-ről anélkül, hogy azt ténylegesen letöltenénk a HTTP HEAD módszer segítségével:
URL url = új URL (FILE_URL); HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection (); httpConnection.setRequestMethod ("HEAD"); long removeFileSize = httpConnection.getContentLengthLong ();
Most, hogy megvan a fájl teljes tartalmi mérete, ellenőrizhetjük, hogy a fájlunk részben letöltődött-e. Ha igen, folytatjuk a letöltést a lemezre rögzített utolsó bájtról:
long existingFileSize = outputFile.length (); if (existingFileSize <fileLength) {httpFileConnection.setRequestProperty ("Tartomány", "bájt =" + existingFileSize + "-" + fájlhossz); }
Ami itt történik, az az konfiguráltuk a URLConnection hogy kérje a fájl bájtjait egy meghatározott tartományban. A tartomány az utoljára letöltött bájttól kezdődik, és a távoli fájl méretének megfelelő bájtnál ér véget.
A. Használatának másik gyakori módja Hatótávolság fejléc egy fájl darabokra való letöltésére szolgál, különböző bájttartományok beállításával. Például 2 KB-os fájl letöltéséhez használhatjuk a 0-1024 és 1024-2048 tartományokat.
Egy másik finom különbség a 2. szakasz kódjától az, hogy a FileOutputStream megnyílik a mellékel paraméter értéke true:
OutputStream os = new FileOutputStream (FILE_NAME, true);
Miután elvégeztük ezt a változtatást, a kód többi része megegyezik a 2. szakaszban láthatóval.
6. Következtetés
Ebben a cikkben többféleképpen láthattunk fájlokat letölteni egy Java URL-ből.
A leggyakoribb megvalósítás az, amelyben az olvasási / írási műveletek végrehajtása során puffereljük a bájtokat. Ez az implementáció biztonságos még nagy fájlok esetén is, mert nem töltjük be a teljes fájlt a memóriába.
Láttuk azt is, hogy miként valósíthatjuk meg a nulla példányszámú letöltést a Java NIO használatával Csatornák. Ez azért hasznos, mert minimálisra csökkentette a bájtok olvasásakor és írásakor végzett kapcsolók számát, és közvetlen pufferek használatával a bájtok nem töltődnek be az alkalmazás memóriájába.
Továbbá, mivel általában egy fájl letöltése HTTP-n keresztül történik, megmutattuk, hogyan érhetjük el ezt az AsyncHttpClient könyvtár segítségével.
A cikk forráskódja elérhető a GitHubon.