הטמעת לקוח FTP בג'אווה

1. סקירה כללית

במדריך זה, נבחן כיצד למנף את ספריית Apache Commons Net לאינטראקציה עם שרת FTP חיצוני.

2. התקנה

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

כיום, בדרך כלל נשתמש ב- Docker בכדי לסייע במערכות אלו לצורך בדיקות האינטגרציה שלנו. עם זאת, במיוחד כאשר משתמשים במצב פסיבי, שרת FTP אינו היישום הקל ביותר להפעלה בשקיפות בתוך מיכל אם אנו רוצים להשתמש במיפוי יציאות דינמי (דבר שנחוץ לעיתים קרובות לבדיקות שניתן להריץ בשרת CI משותף. ).

לכן נשתמש במקום זאת ב- MockFtpServer, שרת FTP מזויף / Stub שנכתב בג'אווה, המספק ממשק API נרחב לשימוש קל במבחני JUnit:

 commons-net commons-net 3.6 org.mockftpserver MockFtpServer 2.7.1 test 

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

3. תמיכה ב- FTP ב- JDK

באופן מפתיע, יש כבר תמיכה בסיסית ב- FTP בכמה טעמי JDK בצורה של sun.net.www.protocol.ftp.FtpURLConnection.

עם זאת, אנחנו לא צריכים להשתמש בכיתה זו ישירות ובמקום זה אפשר להשתמש ב- JDK java.net.מחלקת URL כתמצית.

תמיכה ב- FTP זו היא בסיסית מאוד, אך מנצלת את ממשקי ה- API הנוחים של java.nio.file.Files, זה יכול להספיק למקרי שימוש פשוטים:

@Test ציבורי בטל givenRemoteFile_whenDownloading_thenItIsOnTheLocalFilesystem () זורק IOException {String ftpUrl = String.format ("ftp: // user: [email protected]:% d / foobar.txt", fakeFtpServer.getServerControlPort () URLConnection urlConnection = URL חדש (ftpUrl) .openConnection (); InputStream inputStream = urlConnection.getInputStream (); Files.copy (inputStream, קובץ חדש ("downloaded_buz.txt"). ToPath ()); inputStream.close (); assertThat (קובץ חדש ("downloaded_buz.txt")). קיים (); קובץ חדש ("downloaded_buz.txt"). delete (); // לנקות }

מכיוון שתומכי FTP בסיסיים אלה כבר חסרים תכונות בסיסיות כמו רשימות קבצים, אנו נשתמש בתמיכת FTP בספריית Apache Net Commons בדוגמאות הבאות.

4. מתחבר

ראשית עלינו להתחבר לשרת ה- FTP. נתחיל ביצירת כיתה FtpClient.

זה ישמש ממשק API להפשטה ללקוח ה- FTP Net Apache Commons בפועל:

מחלקה FtpClient {שרת מחרוזות פרטי; נמל אינטר פרטי; משתמש מחרוזת פרטי; סיסמת מחרוזת פרטית; פרטי FTPClient ftp; // בונה חלל פתוח () זורק IOException {ftp = FTPClient חדש (); ftp.addProtocolCommandListener (PrintCommandListener חדש (PrintWriter חדש (System.out))); ftp.connect (שרת, יציאה); תשובה int = ftp.getReplyCode (); אם (! FTPReply.isPositiveCompletion (תשובה)) {ftp.disconnect (); לזרוק IOException חדש ("חריג בחיבור לשרת FTP"); } ftp.login (משתמש, סיסמה); } בטל סגור () זורק IOException {ftp.disconnect (); }}

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

מכיוון שבמבחני האינטגרציה שלנו יהיה מספר קוד boilerplate, כמו הפעלה / עצירה של MockFtpServer וחיבור / ניתוק הלקוח שלנו, אנו יכולים לעשות את הדברים האלה @לפני ו @לאחר שיטות:

מחלקה ציבורית FtpClientIntegrationTest {private FakeFtpServer fakeFtpServer; פרטי FtpClient ftpClient; @ לפני התקנת הריק הציבורי () זורק IOException {fakeFtpServer = FakeFtpServer חדש (); fakeFtpServer.addUserAccount (UserAccount חדש ("משתמש", "סיסמה", "/ נתונים")); FileSystem fileSystem = UnixFakeFileSystem חדש (); fileSystem.add (DirectoryEntry חדש ("/ נתונים")); fileSystem.add (FileEntry חדש ("/ data / foobar.txt", "abcdef 1234567890")); fakeFtpServer.setFileSystem (fileSystem); fakeFtpServer.setServerControlPort (0); fakeFtpServer.start (); ftpClient = FtpClient חדש ("localhost", fakeFtpServer.getServerControlPort (), "משתמש", "סיסמה"); ftpClient.open (); } @ לאחר פירוק חלל פומבי () זורק את IOException {ftpClient.close (); fakeFtpServer.stop (); }}

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

לכן עלינו לאחזר את היציאה בפועל בעת יצירת ה- FtpClient לאחר הפעלת השרת, באמצעות fakeFtpServer.getServerControlPort ().

5. רישום קבצים

מקרה השימוש בפועל הראשון יהיה רישום קבצים.

נתחיל בבדיקה ראשונה בסגנון TDD:

@Test הציבור בטל givenRemoteFile_whenListingRemoteFiles_thenItIsContainedInList () זורק IOException {אוסף קבצים = ftpClient.listFiles (""); assertThat (קבצים) .contains ("foobar.txt"); }

היישום עצמו פשוט באותה מידה. כדי להפוך את מבנה הנתונים שהוחזר מעט פשוט יותר לצורך הדוגמה הזו, אנו הופכים את המוחזר FTPFile מערך הופך לרשימה של מיתרים באמצעות Java 8 זרמים:

אוסף listFiles (נתיב מחרוזת) זורק IOException {FTPFile [] files = ftp.listFiles (path); להחזיר Arrays.stream (קבצים) .map (FTPFile :: getName) .collect (Collectors.toList ()); }

6. הורדה

להורדת קובץ משרת FTP, אנו מגדירים API.

כאן אנו מגדירים את קובץ המקור והיעד במערכת הקבצים המקומית:

@Test ציבורי בטל givenRemoteFile_whenDownloading_thenItIsOnTheLocalFilesystem () זורק IOException {ftpClient.downloadFile ("/ buz.txt", "downloaded_buz.txt"); assertThat (קובץ חדש ("downloaded_buz.txt")). קיים (); קובץ חדש ("downloaded_buz.txt"). delete (); // לנקות }

לקוח ה- FTP של Apache Net Commons מכיל ממשק API נוח, שיכתוב ישירות למוגדר OutputStream. זה אומר שאנחנו יכולים להשתמש בזה ישירות:

void downloadFile (מקור מחרוזת, יעד מחרוזת) זורק IOException {FileOutputStream החוצה = FileOutputStream חדש (יעד); ftp.retrieveFile (מקור, out); }

7. העלאה

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

@Test ציבורי בטל givenLocalFile_whenUploadingIt_thenItExistsOnRemoteLocation () זורק URISyntaxException, IOException {File file = קובץ חדש (getClass (). GetClassLoader (). GetResource ("baz.txt"). ToURI ()); ftpClient.putFileToPath (קובץ, "/buz.txt"); assertThat (fakeFtpServer.getFileSystem (). קיים ("/ buz.txt")). isTrue (); }

העלאת קובץ עובדת ממש בצורה API די דומה להורדתו, אך במקום להשתמש ב- OutputStream, אנחנו צריכים לספק InputStream במקום:

בטל putFileToPath (קובץ קובץ, נתיב מחרוזת) זורק IOException {ftp.storeFile (נתיב, FileInputStream חדש (קובץ)); }

8. מסקנה

ראינו כי השימוש ב- Java יחד עם Apache Net Commons מאפשר לנו, לתקשר בקלות עם שרת FTP חיצוני, לגישה לקריאה וגם לכתיבה.

כרגיל, הקוד השלם למאמר זה זמין במאגר GitHub שלנו.