הורד קובץ מכתובת אתר ב- Java

1. הקדמה

במדריך זה נראה כמה שיטות בהן אנו יכולים להשתמש כדי להוריד קובץ.

נסקור דוגמאות החל משימוש בסיסי ב- Java IO ועד חבילת NIO, וכמה ספריות נפוצות כמו Async Http Client ו- Apache Commons IO.

לבסוף נדבר על האופן שבו נוכל לחדש את ההורדה אם החיבור שלנו נכשל לפני קריאת הקובץ כולו.

2. שימוש ב- Java IO

ה- API הבסיסי ביותר שאנו יכולים להשתמש בו להורדת קובץ הוא Java IO. אנחנו יכולים להשתמש ב- כתובת אתר בכיתה לפתיחת חיבור לקובץ שאנו רוצים להוריד. כדי לקרוא את הקובץ ביעילות, נשתמש ב- openStream () שיטה להשיג InputStream:

BufferedInputStream ב- = BufferedInputStream חדש (URL חדש (FILE_URL) .openStream ())

כשקוראים מתוך InputStream, מומלץ לעטוף אותו בא BufferedInputStream כדי להגדיל את הביצועים.

עליית הביצועים נובעת ממאגר. כשקוראים כל אחד בתים באמצעות ה- לקרוא() שיטה, כל קריאת שיטה מרמזת על שיחת מערכת למערכת הקבצים הבסיסית. כאשר ה- JVM קורא ל- לקרוא() קריאת מערכת, ההקשר של ביצוע התוכנית עובר ממצב משתמש למצב ליבה וחזרה.

מתג ההקשר הזה יקר מנקודת מבט ביצועים. כאשר אנו קוראים מספר רב של בתים, ביצועי היישום יהיו ירודים, עקב מספר רב של מתגי הקשר המעורבים.

לצורך כתיבת הבתים הנקראים מכתובת האתר לקובץ המקומי, נשתמש ב לִכתוֹב() שיטה מה- FileOutputStream מעמד:

נסה (BufferedInputStream ב- = BufferedInputStream חדש (URL חדש (FILE_URL) .openStream ()); FileOutputStream fileOutputStream = FileOutputStream חדש (FILE_NAME)) {data dataBuffer [] = בית חדש [1024]; int bytesRead; בעוד ((bytesRead = in.read (dataBuffer, 0, 1024))! = -1) {fileOutputStream.write (dataBuffer, 0, bytesRead); }} לתפוס (IOException e) {// חריג ידית}

בעת שימוש ב- BufferedInputStream, ה לקרוא() השיטה תקרא בתים רבים ככל שהגדרנו לגודל המאגר. בדוגמה שלנו, אנו כבר עושים זאת על ידי קריאת בלוקים של 1024 בתים בכל פעם, כך BufferedInputStream אינו הכרחי.

הדוגמה שלמעלה מאוד מילולית, אך למרבה המזל, נכון ל- Java 7, יש לנו את קבצים מחלקה המכילה שיטות עוזרות לטיפול בפעולות IO. אנחנו יכולים להשתמש ב- Files.copy () שיטה לקרוא את כל הבתים מ- InputStream והעתק אותם לקובץ מקומי:

InputStream ב- = URL חדש (FILE_URL) .openStream (); Files.copy (ב, Paths.get (FILE_NAME), StandardCopyOption.REPLACE_EXISTING);

הקוד שלנו עובד טוב אך ניתן לשפר אותו. החיסרון העיקרי שלה הוא העובדה כי הבייטים נאגרים בזיכרון.

למרבה המזל, ג'אווה מציעה לנו את חבילת ה- NIO הכוללת שיטות להעברת בתים ישירות בין 2 ערוצים בלי חציצה.

נפרט בפרק הבא.

3. שימוש ב- NIO

חבילת Java NIO מציעה אפשרות להעביר בתים בין 2 ערוצים מבלי להזין אותם בזיכרון היישום.

כדי לקרוא את הקובץ מכתובת האתר שלנו, ניצור קובץ חדש ReadableByteChannel מ ה כתובת אתר זרם:

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

בתים שנקראו מתוך ReadableByteChannel יועבר לא FileChannel המתאים לקובץ שיורד:

FileOutputStream fileOutputStream = FileOutputStream חדש (FILE_NAME); FileChannel fileChannel = fileOutputStream.getChannel ();

נשתמש ב- transferFrom () שיטה מה- ReadableByteChannel בכיתה להורדת הבתים מכתובת האתר הנתונה אל שלנו FileChannel:

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

ה העבר ל() ו transferFrom () השיטות יעילות יותר מאשר קריאה מזרם באמצעות מאגר. בהתאם למערכת ההפעלה הבסיסית, ניתן להעביר את הנתונים ישירות ממטמון מערכת הקבצים לקובץ שלנו מבלי להעתיק בתים לזיכרון היישום.

במערכות לינוקס ו- UNIX, שיטות אלה משתמשות ב- אפס העתק טכניקה המפחיתה את מספר מתגי ההקשר בין מצב הליבה למצב המשתמש.

4. שימוש בספריות

ראינו בדוגמאות לעיל כיצד אנו יכולים להוריד תוכן מכתובת אתר רק באמצעות פונקציונליות הליבה של Java. אנו יכולים גם למנף את הפונקציונליות של הספריות הקיימות כדי להקל על העבודה שלנו, כאשר אין צורך בשינוי ביצועים.

לדוגמא, בתרחיש של העולם האמיתי, נצטרך שקוד ההורדה שלנו יהיה אסינכרוני.

נוכל לעטוף את כל ההיגיון לא ניתן להתקשראו שנוכל להשתמש בספרייה קיימת לשם כך.

4.1. לקוח HTTP Async

AsyncHttpClient היא ספרייה פופולרית לביצוע בקשות HTTP אסינכרוניות באמצעות מסגרת Netty. אנו יכולים להשתמש בה כדי לבצע בקשת GET לכתובת האתר של הקובץ ולקבל את תוכן הקובץ.

ראשית, עלינו ליצור לקוח HTTP:

לקוח AsyncHttpClient = Dsl.asyncHttpClient ();

התוכן שהורד יוכנס ל- a FileOutputStream:

זרם FileOutputStream = FileOutputStream חדש (FILE_NAME);

לאחר מכן, אנו יוצרים בקשת HTTP GET ורושמים AsyncCompletionHandler מטפל לעיבוד התוכן שהורדת:

client.prepareGet (FILE_URL) .execute (AsyncCompletionHandler חדש () {@ מדינת ציבורית ציבורית ב- OnBodyPartReceived (HttpResponseBodyPart bodyPart) זורק חריג {stream.getChannel (). כתוב (bodyPart.getBodyByteBuffer () ON; FileOutputStream onCompleted (תגובה תגובה) זורק חריג {זרם חזרה;}})

שימו לב שדרסנו את onBodyPartReceived () שיטה. יישום ברירת המחדל צובר את נתחי ה- HTTP המתקבלים ל- רשימת מערך. זה יכול להוביל לצריכת זיכרון גבוהה, או OutOfMemory חריג כאשר מנסים להוריד קובץ גדול.

במקום לצבור כל אחד HttpResponseBodyPart בזיכרון, אנו משתמשים ב- FileChannel לכתוב ישירות את הבתים לקובץ המקומי שלנו. נשתמש ב- getBodyByteBuffer () שיטה לגשת לתוכן חלקי הגוף באמצעות א ByteBuffer.

ByteBuffers יש את היתרון שהזיכרון מוקצה מחוץ לערימת ה- JVM, כך שהוא לא משפיע על זיכרון היישומים.

4.2. Apache Commons IO

ספרייה נוספת בשימוש נרחב להפעלת IO היא Apache Commons IO. אנו יכולים לראות מ- Javadoc שיש שם מחלקת שירות FileUtils המשמש למשימות מניפולציה קבצים כלליות.

כדי להוריד קובץ מכתובת אתר, אנו יכולים להשתמש בקו אחד זה:

FileUtils.copyURLToFile (URL חדש (FILE_URL), קובץ חדש (FILE_NAME), CONNECT_TIMEOUT, READ_TIMEOUT);

מנקודת מבט של ביצועים, קוד זה זהה לזה שהדגמנו בסעיף 2.

הקוד הבסיסי משתמש באותם מושגים של קריאה בלולאה כמה בתים מ- InputStream וכותב אותם ל- OutputStream.

הבדל אחד הוא העובדה שכאן חיבור URLC מחלקה משמשת לשליטה על פסק הזמן לחיבור כך שההורדה לא תחסום למשך זמן רב:

חיבור URLConnection = source.openConnection (); connection.setConnectTimeout (connectionTimeout); connection.setReadTimeout (readTimeout);

5. הורדה המתחדשת

בהתחשב בכך שחיבורי אינטרנט נכשלים מעת לעת, כדאי לנו להיות מסוגלים לחדש את ההורדה, במקום להוריד את הקובץ שוב מאפס בתים.

בואו נכתוב את הדוגמה הראשונה מקודם, כדי להוסיף פונקציונליות זו.

הדבר הראשון שעלינו לדעת הוא אנו יכולים לקרוא את גודל הקובץ מכתובת אתר נתונה מבלי להוריד אותו באמצעות שיטת HTTP HEAD:

URL url = URL חדש (FILE_URL); HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection (); httpConnection.setRequestMethod ("HEAD"); ארוך removeFileSize = httpConnection.getContentLengthLong ();

כעת, כשיש לנו את גודל התוכן הכולל של הקובץ, אנו יכולים לבדוק אם הקובץ שלנו הורד חלקית. אם כן, נמשיך את ההורדה מהבית האחרון שהוקלט בדיסק:

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

מה שקורה כאן זה הגדרנו את חיבור URLC לבקש את בתים הקבצים בטווח מסוים. הטווח יתחיל מהבית האחרון שהורד ויסתיים בבייט המתאים לגודל הקובץ המרוחק.

דרך נפוצה נוספת להשתמש ב- טווח כותרת מיועדת להורדת קובץ בנתחים על ידי הגדרת טווחי בתים שונים. לדוגמה, כדי להוריד קובץ 2 KB, נוכל להשתמש בטווח 0 - 1024 ו- 1024 - 2048.

הבדל מעודן נוסף מהקוד בסעיף 2. הוא ש- FileOutputStream נפתח עם לְצַרֵף פרמטר מוגדר נכון:

OutputStream os = FileOutputStream חדש (FILE_NAME, נכון);

לאחר שביצענו את השינוי הזה שאר הקוד זהה לזה שראינו בסעיף 2.

6. מסקנה

ראינו במאמר זה כמה דרכים בהן אנו יכולים להוריד קובץ מ- URL ב- Java.

היישום הנפוץ ביותר הוא זה בו אנו מאגרים את הבייטים בעת ביצוע פעולות הקריאה / כתיבה. יישום זה בטוח לשימוש גם עבור קבצים גדולים מכיוון שאנו לא טוענים את כל הקובץ בזיכרון.

ראינו גם כיצד אנו יכולים ליישם הורדת אפס העתק באמצעות Java NIO ערוצים. זה שימושי מכיוון שהוא הביא למינימום את מספר מתגי ההקשר שנעשו בעת קריאה וכתיבה של בתים ובאמצעות מאגרים ישירים, הבתים אינם נטענים בזיכרון היישום.

כמו כן, מכיוון שבדרך כלל הורדת קובץ מתבצעת באמצעות HTTP, הראינו כיצד אנו יכולים להשיג זאת באמצעות ספריית AsyncHttpClient.

קוד המקור של המאמר זמין באתר GitHub.


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