nio

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 and FileWriter 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 the FileWriter. 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 targetfolder.

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.

Download
You can download the full source code of this example here: Java Nio FTP Example

JJ

Jean-Jay Vester graduated from the Cape Peninsula University of Technology, Cape Town, in 2001 and has spent most of his career developing Java backend systems for small to large sized companies both sides of the equator. He has an abundance of experience and knowledge in many varied Java frameworks and has also acquired some systems knowledge along the way. Recently he has started developing his JavaScript skill set specifically targeting Angularjs and also bridged that skill to the backend with Nodejs.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button