springboot整合ftp

参考链接:https://www.cnblogs.com/callbin/p/15762510.html

阿里云centos7.8搭建ftp服务器

一、简介

vsftpd 全称是:very secure FTP daemon 非常安全的ftp后台程序,及ftp 服务端

二、服务器端部署

1.安装服务 yum install vsftpd -y
 yum install vsftpd -y
2.启动服务
 yum install vsftpd -y
3.启动成功
ps -ef |grep vsftp

请添加图片描述

三、创建cenots 访问用户

我创建的不是虚拟用户(仅供ftp),我创建的是centos登录用户。

1.创建用户目录
mkdir -p /home/wwwroot/ftptest
2.创建用户
 useradd -d /home/wwwroot/ftptest  ftptest   
3.设置目录权限
  • (拥有者设置为ftptest)
chown -R  ftptest  /home/wwwroot/ftptest 
  • (使ftptest用户拥有这个目录的读写权限)
 chmod -R  775  /home/wwwroot/ftptest   
4.设置用户密码
  • (修改ftptest用户密码为123456)
 echo "123456" | passwd --stdin ftptest   

四、在vsftpd服务器中配置用户

1. 查看配置
cat /etc/vsftpd/vsftpd.conf | grep ^[^#]
anonymous_enable=NO
local_enable=YES
write_enable=YES
local_umask=022
anon_upload_enable=YES
anon_mkdir_write_enable=YES
dirmessage_enable=YES
xferlog_enable=YES
connect_from_port_20=YES
xferlog_file=/var/log/xferlog
xferlog_std_format=YES
ascii_upload_enable=YES
ascii_download_enable=YES
chroot_local_user=YES
chroot_list_enable=YES
chroot_list_file=/etc/vsftpd/chroot_list
listen=YES
pam_service_name=vsftpd
userlist_enable=YES
tcp_wrappers=YES
pasv_address=ip # 云服务器ip
pasv_enable=YES
pasv_min_port=30000
pasv_max_port=30910
allow_writeable_chroot=YES

不做字段说明 有兴趣自己百度 (下面链接是自己学习过程需要查看的)。

查看链接

http://t.zoukankan.com/htlee-p-5666802.html   centos添加ftp用户并禁止外切目录
https://blog.51cto.com/u_12674559/2114012   Vsftpd服务配置、ftp命令、错误代码、日志格式
https://www.cnblogs.com/chinaifae/articles/10254729.html 使用java代码上传下载遇到错误时需要查看

五、配置端口

1.vsftpd有两种端口,一个是21端口,用来监听客户端连接请求的。 这个一般说来是固定的,就一直使用21端口。
另一种是,一旦获取到请求之后,再专门用用户服务端和客户端传输数据的端口。

2.编辑配置文件

vi /etc/vsftpd/vsftpd.conf

在最后添加:

pasv_enable=YES
pasv_min_port=30000
pasv_max_port=30010

(表示使用被动模式,用于传输数据的端口分配从30000-30010之间)

六:解决用户鉴权问题

(因为用户 ftptest 是 nologin的,所以存在鉴权的问题。 )

1.编辑pam.d/vsftpd 文件

vi /etc/pam.d/vsftpd 注释掉 #auth required pam_shells.so (这样不去鉴权,从而允许 ftptest 这种 nologin用户登录 ftp 服务器.)

2.编辑shells 文件

vi /etc/shells 增加一行:/sbin/nologin (允许不能登录系统的用户通过鉴权)

七、重启vsftpd服务器

通常重启命令:

service vsftpd restart

centos7改用命令:

systemctl restart vsftpd.service

查看状态:

systemctl status vsftpd.service

八、阿里云服务器还需配置安全组

新增21和 30000/30010端口

九、客户端

1.ftp客户端工具

FileZilla (所有平台)、WinSCP (Windows)、Transmit (Mac OS X)、FireFTP (所有平台与Firefox)、Cyberduck (Mac OS X)、ftprush(Windows)

十、本地cmd测试

tips:下面操作可能需要(我本地测试需要)
1、关闭win防火墙
springboot整合ftp_第1张图片
有上面操作画面 说明 你就成功了。

/etc/vsftpd.chroot_list 为空 限制用户 跳出家目录

请添加图片描述

我设置的。如果你没有成功 ,可能是防火墙。
springboot整合ftp_第2张图片

十一 、工具测试

传输模式选择:主动模式

springboot整合ftp_第3张图片

代码测试

1、测试结果

1、上传

请添加图片描述
springboot整合ftp_第4张图片
默认目录为:/home/wwwroot/ftptest (这是因为我默认用户权限和家目录)
如果设置用户权限和家目录和我不一样,可能需要添加在服务器的绝对目录
例子:/home/wwwroot/ftptest

  public static void main(String[] args) throws IOException {
      FTPClient ftpClient = loginFTP(host, port, username, password);
      uploadFile(ftpClient,"/","1.txt","D:\\1.txt");
  }
2、下载

springboot整合ftp_第5张图片
springboot整合ftp_第6张图片
出现问题,可以打debug测试,查看问题,一般是目录问题。

    public static void main(String[] args) throws IOException {
        FTPClient ftpClient = loginFTP(host, port, username, password);
        dowFile(ftpClient, "/" , "1.txt", "D:\\ftptest\\");
    }

2、代码

   <dependency>
        <groupId>commons-netgroupId>
        <artifactId>commons-netartifactId>
        <version>3.6version>
    dependency>
    <dependency>
        <groupId>org.apache.commonsgroupId>
        <artifactId>commons-lang3artifactId>
    dependency>

源码:我也是百度的 忘记链接了,不好意思

package com.example.cache.ftp;


import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

/**
 * Ftp 工具类
 */
@Slf4j
public class FtpHelper {

    private static String host = "ip";
    private static String username = "用户名";
    private static String password = "密码";
    private static int port = 21;


    /**
     * 登陆FTP并获取FTPClient对象
     *
     * @param host     FTP主机地址
     * @param port     FTP端口
     * @param userName 登录用户名
     * @param password 登录密码
     * @return
     */
    public static FTPClient loginFTP(String host, int port, String userName, String password) {
        FTPClient ftpClient = null;
        try {
            ftpClient = new FTPClient();
            ftpClient.setControlEncoding("UTF-8");// 中文支持
            ftpClient.setConnectTimeout(1000 * 30);//设置连接超时时间
            ftpClient.connect(host, port);// 连接FTP服务器
            ftpClient.login(userName, password);// 登陆FTP服务器
            // 设置文件类型为二进制(如果从FTP下载或上传的文件是压缩文件的时候,不进行该设置可能会导致获取的压缩文件解压失败)
            ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
//            ftpClient.enterLocalPassiveMode();//开启被动模式,否则文件上传不成功,也不报错
            if (!FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) {
                log.info("连接FTP失败,用户名或密码错误。");
                ftpClient.disconnect();
            } else {
                log.info("FTP连接成功!");
            }
        } catch (Exception e) {
            log.info("登陆FTP失败,请检查FTP相关配置信息是否正确!" + e);
            return null;
        }
        return ftpClient;
    }

    /**
     * 从FTP下载文件到本地
     *
     * @param ftpClient     已经登陆成功的FTPClient
     * @param fileName      FTP上的目标文件路径+文件名称
     * @param localFilePath 下载到本地的文件路径
     * @param servicePath   服务器的上面文件的上层路径
     */
    public static String dowFile(FTPClient ftpClient, String servicePath, String fileName, String localFilePath) {
        InputStream is = null;
        FileOutputStream fos = null;
        try {
            ftpClient.enterLocalPassiveMode();
            is = ftpClient.retrieveFileStream(servicePath + fileName);// 获取ftp上的文件
            fos = new FileOutputStream(new File(localFilePath + fileName));
            // 文件读取方式一
            int i;
            byte[] bytes = new byte[1024];
            while ((i = is.read(bytes)) != -1) {
                fos.write(bytes, 0, i);
            }
            // 文件读取方式二
            //ftpClient.retrieveFile(ftpFilePath, new FileOutputStream(new File(localFilePath)));
            ftpClient.completePendingCommand();
            log.info("FTP文件下载成功!");
        } catch (Exception e) {
            log.error("FTP文件下载失败!" + e);
        } finally {
            try {
                if (fos != null) fos.close();
                if (is != null) is.close();
            } catch (IOException e) {
                log.error("下载流关闭失败" + e);
                return null;
            }
        }
        return localFilePath + fileName;
    }

    /**
     * 从FTP下载文件到本地
     *
     * @param ftpClient     已经登陆成功的FTPClient
     * @param fileName      FTP上的目标文件路径+文件名称
     * @param localFilePath 下载到本地的文件路径
     * @param servicePath   服务器的上面文件的上层路径
     */
    public static File downloadFile(FTPClient ftpClient, String servicePath, String fileName, String localFilePath) {
        String name = dowFile(ftpClient, servicePath, fileName, localFilePath);
        if (name != null && !name.equals("")) return new File(fileName);
        else return null;
    }


    /**
     * 上传文件
     *
     * @param serviceDec     ftp服务保存地址
     * @param fileName       上传到ftp的文件名
     * @param originfilename 待上传文件的名称(绝对地址) *
     * @return
     */
    public static boolean uploadFile(FTPClient ftpClient, String serviceDec, String fileName, String originfilename) {
        log.info("开始上传文件");
        try (InputStream input = new FileInputStream(new File(originfilename))) {
            return uploadFile(ftpClient, serviceDec, fileName, input);
        } catch (FileNotFoundException e) {
            log.error("文件上传失败" + e);
        } catch (IOException e) {
            log.error("文件上传失败" + e);
        }
        return false;
    }

    public static void main(String[] args) throws IOException {
        FTPClient ftpClient = loginFTP(host, port, username, password);
        dowFile(ftpClient, "/" , "1.txt", "D:\\ftptest\\");
//        uploadFile(ftpClient,"/","1.txt","D:\\1.txt");
//        FileInputStream fileInputStream = new FileInputStream(new File("D:\\1.xlsx"));
//        int read = fileInputStream.read();
    }

    /**
     * 上传文件
     *
     * @param serviceDec  ftp服务保存地址
     * @param fileName    上传到ftp的文件名
     * @param inputStream 输入文件流
     * @return
     */
    public static boolean uploadFile(FTPClient ftpClient, String serviceDec, String fileName, InputStream inputStream) {
        try {
            log.info("开始上传文件");
            ftpClient.setFileType(ftpClient.BINARY_FILE_TYPE);
            createDirecroty(ftpClient, serviceDec);
            ftpClient.makeDirectory(serviceDec);
            ftpClient.changeWorkingDirectory(serviceDec);
            ftpClient.storeFile(fileName, inputStream);
            inputStream.close();
            ftpClient.logout();
            log.info("上传文件成功");
        } catch (Exception e) {
            log.error("上传文件失败" + e);
        } finally {
            try {
                if (ftpClient.isConnected())
                    ftpClient.disconnect();
                if (null != inputStream)
                    inputStream.close();
            } catch (IOException e) {
                log.error("上传文件失败" + e);
                return false;
            }
        }
        return true;
    }


    //改变目录路径
    private static boolean changeWorkingDirectory(FTPClient ftpClient, String directory) {
        boolean flag = true;
        try {
            flag = ftpClient.changeWorkingDirectory(directory);
            if (flag) {
                log.info("进入文件夹" + directory + " 成功!");

            } else {
                log.info("进入文件夹" + directory + " 失败!开始创建文件夹");
            }
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
        return flag;
    }

    //创建多层目录文件,如果有ftp服务器已存在该文件,则不创建,如果无,则创建
    private static boolean createDirecroty(FTPClient ftpClient, String remote) throws IOException {
        boolean success = true;
        String directory = remote + "/";
        // 如果远程目录不存在,则递归创建远程服务器目录
        if (!directory.equalsIgnoreCase("/") && !changeWorkingDirectory(ftpClient, new String(directory))) {
            int start = 0;
            int end = 0;
            if (directory.startsWith("/")) {
                start = 1;
            } else {
                start = 0;
            }
            end = directory.indexOf("/", start);
            String path = "";
            String paths = "";
            while (true) {
                String subDirectory = new String(remote.substring(start, end).getBytes("GBK"), "iso-8859-1");
                path = path + "/" + subDirectory;
                if (!existFile(ftpClient, path)) {
                    if (makeDirectory(ftpClient, subDirectory)) {
                        changeWorkingDirectory(ftpClient, subDirectory);
                    } else {
                        log.info("创建目录[" + subDirectory + "]失败");
                        changeWorkingDirectory(ftpClient, subDirectory);
                    }
                } else {
                    changeWorkingDirectory(ftpClient, subDirectory);
                }

                paths = paths + "/" + subDirectory;
                start = end + 1;
                end = directory.indexOf("/", start);
                // 检查所有目录是否创建完毕
                if (end <= start) {
                    break;
                }
            }
        }
        return success;
    }

    //判断ftp服务器文件是否存在
    private static boolean existFile(FTPClient ftpClient, String path) throws IOException {
        boolean flag = false;
        FTPFile[] ftpFileArr = ftpClient.listFiles(path);
        if (ftpFileArr.length > 0) {
            flag = true;
        }
        return flag;
    }

    //创建目录
    private static boolean makeDirectory(FTPClient ftpClient, String dir) {
        boolean flag = true;
        try {
            flag = ftpClient.makeDirectory(dir);
            if (flag) {
                log.info("创建文件夹" + dir + " 成功!");

            } else {
                log.info("创建文件夹" + dir + " 失败!");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return flag;
    }

    /**
     * 获取FTP某一特定目录下的所有文件名称
     *
     * @param ftpClient  已经登陆成功的FTPClient
     * @param ftpDirPath FTP上的目标文件路径
     */
    public static List getFileNameList(FTPClient ftpClient, String ftpDirPath) {
        List list = new ArrayList();
        try {
            if (ftpDirPath.startsWith("/") && ftpDirPath.endsWith("/")) {
                // 通过提供的文件路径获取FTPFile对象列表
                FTPFile[] files = ftpClient.listFiles(ftpDirPath);
                // 遍历文件列表,打印出文件名称
                for (int i = 0; i < files.length; i++) {
                    FTPFile ftpFile = files[i];
                    // 此处只打印文件,未遍历子目录(如果需要遍历,加上递归逻辑即可)
                    if (ftpFile.isFile()) {
//                        log.info(ftpDirPath + ftpFile.getName());
                        list.add(ftpFile.getName());
                    }
                }
                log.info("当前FTP路径可用");
            } else {
                log.info("当前FTP路径不可用");
            }
        } catch (IOException e) {
            log.error("错误" + e);
        }
        return list;
    }

    /**
     * 获取到服务器文件夹里面最新创建的文件名称
     *
     * @param ftpDirPath 文件路径
     * @param ftpClient  ftp的连接
     * @return fileName
     */
    public static String getNewFile(FTPClient ftpClient, String ftpDirPath) throws Exception {
        if (ftpDirPath.startsWith("/") && ftpDirPath.endsWith("/")) {
            // 通过提供的文件路径获取FTPFile对象列表
            FTPFile[] files = ftpClient.listFiles(ftpDirPath);
            if (files == null) throw new Exception("文件数组为空");
//            Arrays.sort(files, new Comparator() {
//                public int compare(FTPFile f1, FTPFile f2) {
//                    return f1.getTimestamp().compareTo(f2.getTimestamp());
//                }
//                public boolean equals(Object obj) {
//                    return true;
//                }
//            });
            return ftpDirPath + "/" + files[files.length - 1].getName();
        } else {
            throw new Exception("文件夹路径错误!");
        }
    }
}

完结!继续学习!
源码网上百度一大堆!

遇坑

1.本地win和代码测试的时候 没有关防火墙,总是失败,或者上传下载太慢了,可能就是这个原因导致

2.上传下载失败。代码报错,可能原因,用户权限,目录权限。

可以根据代码报错查看原因。

有问题或者侵权请及时联系,我会及时删除。

你可能感兴趣的:(java,spring,boot,后端,java)