Java Nio FTP Example
In this example we will demonstrate an FTP example program written in Java using some of the NIO features available to us.
The example program will take the form of a command line program which accepts 2 arguments, namely the FTP location to transfer the file from and the local destination on the file system, inclusive of the file name, of where to save the file.
We will configure our local operating system to serve a specific directory via FTP. In order to simulate the FTP server we will make use of the vsftpd
ubuntu
package.
1. Introduction
In this example we make use of a URL object to specify the FTP location (including authentication credentials).
A FileChannel is used for writing the contents of the file to local disk, by calling one of the open(..)
methods of the FileChannel class.
We provide the Path to the File and indicate the StandardOpenOption#WRITE and StandardOpenOption#CREATE modes when opening.
We also make use of a ReadableByteChannel to open a Channel to the FTP location and read the contents untill end.
2. Technologies used
The example code in this article was built and run using:
- Java 8
- Maven 3.3.9
- STS (3.9.0.RELEASE)
- Ubuntu 16.04
- vsftpd (package)
3. Setup
To ensure that Maven and Java are installed you can execute the following:
Confirm Java and Maven
java -version java version "1.8.0_101" Java(TM) SE Runtime Environment (build 1.8.0_101-b13) Java HotSpot(TM) 64-Bit Server VM (build 25.101-b13, mixed mode) mvn -version Apache Maven 3.3.9 Maven home: /usr/share/maven Java version: 1.8.0_101, vendor: Oracle Corporation Java home: /home/jean-jay/runtimes/jdk1.8.0_101/jre Default locale: en_ZA, platform encoding: UTF-8 OS name: "linux", version: "4.10.0-42-generic", arch: "amd64", family: "unix"
4. Setting up an FTP server
To setup a local FTP server we will make use of vsftpd
. If not already setup we can install and configure it via the command line:
Installing vsftpd
# Install vsftpd sudo apt-get update sudo apt-get install vsftpd # Backup configuration sudo cp /etc/vsftpd.conf /etc/vsftpd.conf.orig # Ensure firewall inactive sudo systemctl stop ufw # Setup ftp configuration sudo vim /etc/vsftpd.conf # Ensure the following settings are present anonymous_enable=NO local_enable=YES userlist_enable=YES userlist_file=/etc/vsftpd.userlist userlist_deny=NO # Save the file # Add current user to userlist echo jean-jay | sudo tee -a /etc/vsftpd.userlist # Restart service sudo systemctl restart vsftpd # Test it ftp -p localhost Connected to localhost. 220 (vsFTPd 3.0.3) Name (localhost:jean-jay): 331 Please specify the password. Password: 230 Login successful. Remote system type is UNIX. Using binary mode to transfer files. ftp
5. The Code
The 3 core classes in the program include NIOftp
, FileReader
and FileWriter
.
NIOftp
public final class NIOftp { private static final int FTP_BUFFER_CAPACITY = 2048; private NIOftp() { throw new IllegalStateException(Constants.INSTANTIATION_NOT_ALLOWED); } public static void ftp(final URL from, final String target) throws IOException { if (Objects.isNull(from)) { throw new IllegalArgumentException("URL required for ftp source"); } if (StringUtils.isBlank(target)) { throw new IllegalArgumentException("target required"); } final FileReader reader = createReader(from); final FileWriter writer = createWriter(target); try { System.out.printf("Starting ftp from %s\n", from.toString()); ByteBuffer buffer = ByteBuffer.allocate(FTP_BUFFER_CAPACITY); while (reader.read(buffer) >= 0) { writer.write((ByteBuffer) buffer.flip()); buffer.clear(); } } finally { System.out.println("Completed transfer"); reader.close(); writer.close(); } } private static FileReader createReader(final URL from) throws IOException { assert !Objects.isNull(from); return new FileReader(from); } private static FileWriter createWriter(final String target) throws IOException { assert StringUtils.isNotBlank(target); return new FileWriter(target); } }
- lines 18-19: we create the
FileReader
andFileWriter
instances respectively - lines 21-35: we allocate a ByteBuffer and read the content from the FTP location into said ByteBuffer. We then
flip
the ByteBuffer to prepare it for reading and delegate that task on to theFileWriter
. The ByteBuffer is then cleared for the next iteration.
FileReader
final class FileReader { private final ReadableByteChannel from; FileReader(final URL url) throws IOException { this.from = Channels.newChannel(url.openStream()); } int read(final ByteBuffer buffer) throws IOException { return this.from.read(buffer); } void close() throws IOException { Objects.requireNonNull(this.from).close(); } }
- The
FileReader
encapsulates the reading of the remote file.
FileWriter
final class FileWriter { private final FileChannel target; FileWriter(final String path) throws IOException { this.target = FileChannel.open(Paths.get(path), StandardOpenOption.WRITE, StandardOpenOption.CREATE); } void write(final ByteBuffer buffer) throws IOException { this.target.write(buffer); while (buffer.hasRemaining()) { buffer.compact(); this.target.write(buffer); } } void close() throws IOException { Objects.requireNonNull(this.target).close(); } }
- The
FileWriter
encapsulates the writing of the file to local file system
6. Running the Program
Ensure vsftpd is running
Check status of vsftpd
sudo systemctl status vsftpd [sudo] password for jean-jay: vsftpd.service - vsftpd FTP server Loaded: loaded (/lib/systemd/system/vsftpd.service; enabled; vendor preset: enabled) Active: active (running) since Sat 2017-12-23 06:15:22 CET; 7h ago Process: 1210 ExecStartPre=/bin/mkdir -p /var/run/vsftpd/empty (code=exited, status=0/SUCCESS) Main PID: 1222 (vsftpd) Tasks: 1 Memory: 2.3M CPU: 150ms CGroup: /system.slice/vsftpd.service └─1222 /usr/sbin/vsftpd /etc/vsftpd.conf
Then navigate to the home directory of the current user, on my system that would be /home/jean-jay
.
Then place a file that you would like to ftp in that location eg: docs.zip
as this is the folder that vsftpd
will be serving.
Then navigate to the root folder of where you downloaded the example project. Execute mvn clean install package
and then navigate into the target
folder of said project. Once there execute the following:
Running program (replace username and password with proper values)
java -jar nio_ftp_example-0.0.1-SNAPSHOT.jar -u ftp://<username>:<password>@localhost/docs.zip -t /home/jean-jay/Documents/github-projects/codegeeks/nio-ftp-example/target/docs.zip Starting ftp from ftp://<username>:<password>@localhost/docs.zip Completed transfer
You can then confirm your ftp by checking the file in your target
folder.
7. Summary
In this example we briefly covered some of the core abstractions used in making this example. We also demonstrated the use of said example program to FTP a file from a local FTP server serving a directory from our file system.
8. Download the Source Code
This was a Java Nio FTP Example.
You can download the full source code of this example here: Java Nio FTP Example