配置FTP环境

1. 安装并启动 FTP 服务

安装VSFTPD

使用 apt-get 安装 [vsftpd]:

sudo apt-get install vsftpd -y

vsftpd 是在 Linux 上被广泛使用的 FTP 服务器,根据其官网介绍,它可能是 UNIX-like 系统下最安全和快速的 FTP 服务器软件。

启动VSFTPD

安装完成后 VSFTPD 会自动启动,通过 netstat 命令可以看到系统已经

sudo netstat -nltp | grep 21

如果没有启动,可以手动开启 VSFTPD 服务:

sudo systemctl start vsftpd.service # FTP协议默认使用21端口作为服务端口

2. 配置用户访问目录

新建用户主目录

sudo mkdir /home/uftp

执行完后,在这里 /home/uftp [?] 就能看到新建的文件夹 uftp 了。

创建登录欢迎文件 [?]:

sudo touch /home/uftp/welcome.txt

方便用户登录后可以看到欢迎信息,并且确定用户确实登录到了主目录上。

用户的主目录是用户通过 FTP 登录后看到的根目录

新建用户 uftp 并设置密码

sudo useradd -d /home/uftp -s /bin/bash uftp  # 创建一个用户
sudo passwd uftp # 为用户设置密码
sudo rm /etc/pam.d/vsftpd  # 删除掉 pam.d 中 vsftpd,因为该配置文件会导致使用用户名登录 ftp 失败:

限制该用户仅能通过 FTP 访问

sudo usermod -s /sbin/nologin uftp

修改 vsftpd 配置

sudo chmod a+w /etc/vsftpd.conf
# 修改vsftpd配置  /etc/vsftpd.conf
# 限制用户对主目录以外目录访问
chroot_local_user=YES

# 指定一个 userlist 存放允许访问 ftp 的用户列表
userlist_deny=NO
userlist_enable=YES
# 记录允许访问 ftp 用户列表
userlist_file=/etc/vsftpd.user_list
# 不配置可能导致莫名的530问题
seccomp_sandbox=NO
# 允许文件上传
write_enable=YES
# 使用utf8编码
utf8_filesystem=YES

新建文件 /etc/vsftpd.user_list,用于存放允许访问 ftp 的用户:

sudo touch /etc/vsftpd.user_list
sudo chmod a+w /etc/vsftpd.user_list
# 修改 /etc/vsftpd.user_list
sudo touch /etc/vsftpd.user_list
sudo chmod a+w /etc/vsftpd.user_list
# 修改 /etc/vsftpd.user_list ,加入刚刚创建的用户:
uftp

设置主目录访问权限

sudo chmod a-w /home/uftp
sudo mkdir /home/uftp/public && sudo chmod 777 -R /home/uftp/public
sudo systemctl restart vsftpd.service

3. 访问方式

可以通过windows的资源管理器进行访问

ftp://106.52.107.51/
ftp://ftpuser:Password@<您的 CVM IP 地址>

java方式连接

FTP工具类:

package com.learning.ftp.util;


import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.apache.log4j.Logger;
import org.yaml.snakeyaml.tokens.FlowEntryToken;
import sun.net.ftp.FtpClient;

import java.io.*;

/**
 * @Author:siler
 * @Date:2022/2/16 16:06
 */
public class FTPUtilsByApache {

    Logger logger  =  Logger.getLogger(FTPUtilsByApache.class );
    private String encoding = System.getProperty("file.encoding");
    private FTPClient ftpClient =null;
    private String url;
    private int port;
    private String username;
    private String password;

    /**
     * @Date: 2022/2/16 16:08
     * @Description: 默认为21端口
    **/
    public FTPUtilsByApache(String url,String username,String password){
        this.url=url;
        this.port=21;
        this.username=username;
        this.password=password;
    }

    public FTPUtilsByApache(String url,int port,String username,String password){
        this.url=url;
        this.port=port;
        this.username=username;
        this.password=password;
    }

    public FTPClient getFTPClient(){
        int reply;
        if(this.ftpClient == null || !this.ftpClient.isConnected()){
            this.ftpClient=new FTPClient();
            try{
                this.ftpClient.connect(url,port);
                boolean login = this.ftpClient.login(username, password);
                if(login){
                    logger.info("FTP连接成功!");
                }
                // 设置文件传输类型为二进制传输
                this.ftpClient.setFileType(ftpClient.BINARY_FILE_TYPE);
                this.ftpClient.setControlEncoding(encoding);
                reply = ftpClient.getReplyCode();
                if(!FTPReply.isPositiveCompletion(reply)){
                    ftpClient.disconnect();
                    return null;
                }
                return this.ftpClient;
            }catch (IOException e){
                e.printStackTrace();
                logger.info("获取连接失败");
                return null;
            }
        }else{
            return this.ftpClient;
        }
    }

    /**
     * @Date: 2022/2/16 16:26
     * @Description: 指定ftp文件名下载到本地
     * @param remotePath ftp目录
     * @param fileName 指定下载的文件
     * @param localPath 本地目录
    **/

    public boolean downFile(String remotePath, String fileName,String localPath){
        boolean result=false;
        getFTPClient();
        if(!isCreateFtpClient(this.ftpClient)){
            return false;
        }
        try{
            this.ftpClient.changeWorkingDirectory(remotePath);
            FTPFile[] remoteFiles = this.ftpClient.listFiles();
            for(FTPFile rf:remoteFiles){
                if(rf.getName().equals(fileName)){
                    logger.info("获取到"+fileName+"文件");
                    // 创建本地存储路径
                    File lf = new File(localPath + File.separator + rf.getName());
                    FileOutputStream lfos = new FileOutputStream(lf);
                    // 通过文件检索系统,将文件写入流
                    this.ftpClient.retrieveFile(rf.getName(),lfos);
                    System.out.println("下载完毕");
                    lfos.close();
                }
            }
            this.ftpClient.logout();
            result=true;
        }catch (IOException e){
            e.printStackTrace();
            return result;
        }finally {
            if(ftpClient.isConnected()){
                try{
                    ftpClient.disconnect();
                }catch (Exception ee){
                    ee.printStackTrace();
                }
            }
        }
        return result;
    }

    /**
     * @Date: 2022/2/16 16:42
     * @Description: 下载所有文件到本地目录,不包含目录
     * @param remotePath ftp目录
     * @param localPath 本地目录
     * @param prefix  前缀
     * @param suffix 后缀
    **/
    public boolean downAllPathFile(String remotePath, String localPath,String prefix,String suffix){
        boolean result=false;
        if(prefix==null||prefix.trim().equals("")){
            //return this.downAllPathFileSuffix(remotePath, localPath, suffix);
            return result;
        }else if(suffix==null||suffix.trim().equals("")){
            //return this.downAllPathFilePrefix(remotePath, localPath, prefix);
            return result;
        }else{
            getFTPClient();
            if(!isCreateFtpClient(this.ftpClient)){
                return false;
            }
            try{
                this.ftpClient.changeWorkingDirectory(remotePath);
                FTPFile[] remoteFiles = this.ftpClient.listFiles();
                for(FTPFile rf:remoteFiles){
                    if(rf.isFile()&&rf.getName().contains(prefix)&&rf.getName().endsWith(suffix)){
                        logger.info("获取到"+rf.getName()+"文件");
                        File lf = new File(localPath + "/" + rf.getName());
                        FileOutputStream lfos = new FileOutputStream(lf);
                        // 通过文件检索系统,将文件写入流
                        this.ftpClient.retrieveFile(rf.getName(),lfos);
                        lfos.close();
                    }
                }
                this.ftpClient.logout();
                result=true;
            }catch (IOException e){
                e.printStackTrace();
                return result;
            }finally {
                if(ftpClient.isConnected()){
                    try{
                        ftpClient.disconnect();
                    }catch (Exception ee){
                        ee.printStackTrace();
                    }
                }
            }
            return result;
        }
    }


    /**
     * @Date: 2022/2/16 16:51
     * @Description: 上传文件
    **/
    public boolean uploadFile(String remotePath, String filename, InputStream lin){
        boolean result=false;
        getFTPClient();
        if(!this.isCreateFtpClient(this.ftpClient)){
            return false;
        }
        try{
            boolean change = ftpClient.changeWorkingDirectory(remotePath);
            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
            result = ftpClient.storeFile(new String(filename.getBytes(encoding),"iso-8859-1"), lin);

            if(change){}
            logger.info("上传完毕,结果为"+result);
            lin.close();
            ftpClient.logout();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if(ftpClient.isConnected()){
                try{
                    ftpClient.disconnect();
                }catch (Exception ee){
                    ee.printStackTrace();
                }
            }
        }
        return result;
    }

    /**
     * @Date: 2022/2/16 16:56
     * @Description: FTP文件上传
     * @param remotePath
     * @param localFilePath 完整的本地路径
    **/
    public boolean uploadFile(String remotePath,String localFilePath) {
        File lf=new File(localFilePath);
        try{
            FileInputStream lfis = new FileInputStream(lf);
            return uploadFile(remotePath,lf.getName(),lfis);
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }

    /**
     * @Date: 2022/2/16 17:01
     * @Description: 删除远程文件信息
    **/
    public boolean deleteFile(String filePath,String fileName){
        getFTPClient();
        boolean result = false;
        if(isCreateFtpClient(this.ftpClient)){
            try{
                ftpClient.changeWorkingDirectory(filePath);
                result = ftpClient.deleteFile(new String(fileName.getBytes(encoding),"iso-8859-1"));
            }catch (IOException e){
                e.printStackTrace();
                return result;
            }finally {
                if(ftpClient.isConnected()){
                    try{
                        ftpClient.disconnect();
                    }catch (Exception ee){
                        ee.printStackTrace();
                    }
                }
            }
        }
        return result;
    }


    /*
     * @Date: 2022/2/16 16:30
     * @Description: 是否创建ftp客户端
    **/
    public boolean isCreateFtpClient(FTPClient c){
        if(c == null|| !c.isConnected()){
            return false;
        }else{
            return true;
        }

    }
}

测试类:

public static void main(String[] args) throws FileNotFoundException {
        String hostname = "106.52.107.51";
        int port = 21;
        String username = "ftpuser";
        String password = "ZkAk_OgK";
        String workingPath = "/pub";
        String str = "E:\\game\\天气情况.log";
        String loaclPath="E:\\SoftWare";
        InputStream fileInputStream = new FileInputStream(new File(str));
        String saveName = "天气情况.log";
        String downName="天气情况.log";
        FTPUtilsByApache utils = new FTPUtilsByApache(hostname,port,username,password);
        utils.getFTPClient();
        // 上传方法测试
        //utils.uploadFile(workingPath,str);
        // 下载方法测试
        //System.out.println(utils.downFile(workingPath,downName,loaclPath));
        // 删除方法测试
        System.out.println(utils.deleteFile(workingPath,downName));

        //System.out.println(FtpUtil.upload( hostname, port, username, password, workingPath, fileInputStream, saveName));
    }