SpringBoot集成FTP实现文件上传下载_引入ftpjar包-程序员宅基地

技术标签: spring boot  FTP  java  ftp  SpringBoot  

SpringBoot集成FTP实现文件上传下载

安装vsftpd

阿里云CentOS7.6手动安装ftp

开始集成ftp

1.首先引入jar包

我用的是gradle 换成maven的话还是同样的包,换成对应的maven里面的写法就行了


dependencies {
    
    implementation 'org.springframework.boot:spring-boot-starter'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    implementation group: 'commons-net', name: 'commons-net', version: '3.8.0'
    compileOnly group: 'javax.servlet', name: 'javax.servlet-api', version: '4.0.1'
    compileOnly group: 'org.projectlombok', name: 'lombok', version: '1.18.20'
    implementation group: 'io.netty', name: 'netty-common', version: '4.1.68.Final'

}

2.创建工具类


package com.dnydys.util;

import io.netty.util.CharsetUtil;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPClientConfig;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;

/**
 * @Classname FtpUtil
 * @Description TODO
 * @Date 2021/12/17 19:52
 * @Created by hasee
 */
public class FtpUtil {
    

    public FTPClient ftpClient = new FTPClient();
    //ftp服务器地址
    public static final String HOSTNAME = "101.133.226.94";
    //ftp服务器端口号
    public static final int PORT = 21;
    //登录ftp服务器的用户名和密码
    public static final String USERNAME = "ftptest";
    public static final String PWD = "1a2s3d4f";
    //ftpuser的基础目录
    //所有用户的文件都是在这个目录下
//    public static final String BASEPATH = "/var/ftp/test";

    //本地字符编码
    static String LOCAL_CHARSET = "UTF-8";
    // FTP协议里面,规定文件名编码为iso-8859-1
    static String SERVER_CHARSET = "ISO-8859-1";


    /**
     * @className FtpUtil
     * @author dnydys
     * @description 用于登录ftp服务器
     * @updateTime 2021/12/17 20:14
     * @return: void
     * @version 1.0
     */
    public Boolean ftpLogin() throws IOException {
    

        ftpClient.setAutodetectUTF8(true);
        ftpClient.setCharset(CharsetUtil.UTF_8);
        ftpClient.setControlEncoding(CharsetUtil.UTF_8.name());
        //服务器地址和端口
        ftpClient.connect(HOSTNAME,PORT);

        //登录的用户名和密码
        Boolean isLogin = ftpClient.login(USERNAME, PWD);
        return isLogin;
    }

    /**
     * @className FtpUtil
     * @author dnydys
     * @description 用于退出ftp服务器
     * @updateTime 2021/12/17 20:15
     * @return: void
     * @version 1.0
     */
    public Boolean ftpLogout() throws IOException {
    
        Boolean isLogout = ftpClient.logout();
        return isLogout;
    }

    /**
     * @className FtpUtil
     * @author dnydys
     * @description 切换目录至基础目录
     * @updateTime 2021/12/17 21:53
     * @return: java.lang.Boolean
     * @version 1.0
     */
    public Boolean changeWorkingDirectory() throws IOException {
    
        String BASEPATH = "/var/ftp/0000";
        boolean b = ftpClient.changeWorkingDirectory(BASEPATH);
        return b;
    }

    /**
     * @className FtpUtil
     * @author dnydys
     * @description 切换目录至用户目录
     * @updateTime 2021/12/17 21:54
     * @return: java.lang.Boolean
     * @version 1.0
     */
    public Boolean changeWorkingDirectory(String userPath) throws IOException {
    
        boolean b = ftpClient.changeWorkingDirectory(userPath);
        return b ;
    }

    /**
     * @className FtpUtil
     * @author dnydys
     * @description 创建用户目录 false说明用户目录已经存在
     * @updateTime 2021/12/17 21:59
     * @return: java.lang.Boolean
     * @version 1.0
     */
    public Boolean makeDirectory(String userid) throws IOException {
    
        boolean b = ftpClient.makeDirectory(userid);
        return b;
    }

    /**
     * @className FtpUtil
     * @author dnydys
     * @description 设置文件类型 二进制形式传文件
     * @updateTime 2021/12/17 22:07
     * @return: java.lang.Boolean
     * @version 1.0
     */
    public Boolean setFileType() throws IOException {
    
        return ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
    }

    /**
     * @className FtpUtil
     * @author dnydys
     * @description 1.服务器端保存的文件名,2.上传文件的inputstream
     * @updateTime 2021/12/17 22:24
     * @return: java.lang.Boolean
     * @version 1.0
     */
    public Boolean storeFile(String filename, FileInputStream inputStream) throws IOException {
    
//         开启服务器对UTF-8的支持,如果服务器支持就用UTF-8编码,否则就使用本地编码(GBK).
//        if (FTPReply.isPositiveCompletion(ftpClient.sendCommand("OPTS UTF8", "ON"))) {
    
//            LOCAL_CHARSET = "UTF-8";
//        }
//        ftpClient.setControlEncoding(LOCAL_CHARSET);
//        return ftpClient.storeFile(new String(filename.getBytes(LOCAL_CHARSET),
//            SERVER_CHARSET),inputStream);

        return ftpClient.storeFile(filename,inputStream);
    }

    /**
     * @className FtpUtil
     * @author dnydys
     * @description 打印用户当前所在目录
     * @updateTime 2021/12/18 9:59
     * @return: java.lang.String
     * @version 1.0
     */
    public String printWorkingDirectory() throws IOException {
    
        return ftpClient.printWorkingDirectory();
    }
    /**
     * @className FtpUtil
     * @author dnydys
     * @description
     * @updateTime 2021/12/18 10:20 
     * @return: void

     * @version 1.0
     */
    public void enterLocalPassiveMode(){
    
//        每次数据连接之前ftp client告诉ftp server开通 一个端口来传输数据。
//        原因是ftp每次开启不同的端口传输数据,linux上,安全限制,某些端口没有开启,出现阻塞
//        但是不理解他说的被动和主动方式的含义。
//        今天回来查资料知道:
//        FTP协议有两种工作方式,Port和Pasv方式,主动和被动式。
//        (1) PORT(主动模式)
//        工作的原理: FTP客户端连接到FTP服务器的21端口,发送用户名和密码登录,登录成功后要list列表或者读取数据时,客户端随机开放一个端口(1024以上),发送 PORT命令到FTP服务器,告诉服务器客户端采用主动模式并开放端口;FTP服务器收到PORT主动模式命令和端口号后,通过服务器的20端口和客户端开放的端口连接,发送数据
//            (2) PASV(被动模式) 这就是上面方法的作用。
//        工作的原理:FTP客户端连接到FTP服务器的21端口,发送用户名和密码登录,登录成功后要list列表或者读取数据时,发送PASV命令到FTP服务器, 服务器在本地随机开放一个端口(1024以上),然后把开放的端口告诉客户端, 客户端再连接到服务器开放的端口进行数据传输
        ftpClient.enterLocalPassiveMode();
    }

    /**
     * @className FtpUtil
     * @author dnydys
     * @description 查询所有文件列表
     * @updateTime 2021/12/18 22:05
     * @return: void
     * @version 1.0
     */
    public FTPFile[] listFiles() throws IOException {
    
        if (FTPReply.isPositiveCompletion(ftpClient.sendCommand("OPTS UTF8", "ON"))) {
    
            LOCAL_CHARSET = "UTF-8";
        }
        ftpClient.setControlEncoding(LOCAL_CHARSET);
        return ftpClient.listFiles();
    }
    
    /**
     * @className FtpUtil
     * @author dnydys
     * @description 检索文件
     * @updateTime 2021/12/18 22:12 
     * @return: java.lang.Boolean
     * @version 1.0
     */
    public Boolean retrieveFile(String fileName, FileOutputStream outputStream) throws IOException {
    
        return ftpClient.retrieveFile(fileName, outputStream);
    }

    /**
     * @className FtpUtil
     * @author dnydys
     * @description
     * @updateTime 2021/12/18 23:07
     * @return: null
     * @version 1.0
     */
    public InputStream retrieveFileStream(String fileName) throws IOException {
    
        return ftpClient.retrieveFileStream(fileName);
    }


}


3.创建ServiceImpl

service我就不放了


package com.dnydys.service.impl;

import com.dnydys.common.baseRespone.ResultInfo;
import com.dnydys.common.code.ResponseCode;
import com.dnydys.service.FileUploadService;
import com.dnydys.util.FtpUtil;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.FTPFile;
import org.springframework.stereotype.Service;

/**
 * @Classname FileUploadServiceImpl
 * @Description TODO
 * @Date 2021/12/17 20:26
 * @Created by hasee
 */
@Service
@Slf4j
public class FileUploadServiceImpl implements FileUploadService {
    


    @Override
    public ResultInfo fileUpload(String localPath,String userid,String fileName) throws IOException {
    
        ResultInfo resultInfo = new ResultInfo();
        FtpUtil ftpUtil = new FtpUtil();
        Boolean aBoolean = ftpUtil.ftpLogin();
        if (!aBoolean){
    
            log.error("FTP用户登录失败");
            resultInfo.setResultCode(ResponseCode.FTP_LOGIN_ERROR);
            return resultInfo;
        }

//        localPath = new String(localPath.getBytes("GBK"),"UTF-8");
//        fileName = new String(fileName.getBytes("GBK"),"UTF-8");
        FileInputStream inputStream = new FileInputStream(new File(localPath));
        //ftptest用户登陆上去之后默认是在BasePath之下,所以不用切换,后续不是BasePath的话再放开就行了
//        Boolean isChangeBaseSuccess= ftpUtil.changeWorkingDirectory();
//        if (!isChangeBaseSuccess){
    
//            log.error("FTP切换基础目录失败");
//            resultInfo.setResultCode(ResponseCode.FTP_CHANGE_BASEPATH_ERROR);
//            return resultInfo;
//        }

        Boolean isChangeUserSuccess = ftpUtil.changeWorkingDirectory(userid);
        if (!isChangeUserSuccess){
    
            log.error("FTP切换用户目录失败,该用户目录不存在,先创建用户目录");
            Boolean isMakeDirectory = ftpUtil.makeDirectory(userid);
            if (!isMakeDirectory){
    
                log.error(userid+"用户目录创建失败");
                resultInfo.setResultCode(ResponseCode.FTP_LOGIN_ERROR);
                return resultInfo;
            }
        }
        Boolean isStoreFile = ftpUtil.storeFile(fileName, inputStream);
        if(!isStoreFile){
    
            log.error("FTP服务器端上传文件失败");
            resultInfo.setResultCode(ResponseCode.FTP_STOREFILE_ERROR);
            return resultInfo;
        }
        Boolean isLogout = ftpUtil.ftpLogout();
        if (!isLogout){
    
            log.error("FTP服务器登出失败");
            resultInfo.setResultCode(ResponseCode.FTP_LOGOUT_ERROR);
            return resultInfo;
        }
        resultInfo.setResultCode(ResponseCode.SUCCESS);
        return resultInfo;
    }

    @SneakyThrows
    @Override
    public ResultInfo fileDownload(String localPath,String userid,String fileName) {
    
        ResultInfo resultInfo = new ResultInfo();
        FtpUtil ftpUtil = new FtpUtil();
        Boolean isLogin = ftpUtil.ftpLogin();
        if (!isLogin){
    
            log.error("FTP用户登录失败");
            resultInfo.setResultCode(ResponseCode.FTP_LOGIN_ERROR);
            return resultInfo;
        }

        Boolean isChangeUserSuccess = ftpUtil.changeWorkingDirectory(userid);
        if (!isChangeUserSuccess){
    
            log.error("FTP切换用户目录失败");
            log.error("FTP切换用户目录失败,该用户目录不存在,先创建用户目录");
            Boolean isMakeDirectory = ftpUtil.makeDirectory(userid);
            if (!isMakeDirectory){
    
                log.error(userid+"用户目录创建失败");
                resultInfo.setResultCode(ResponseCode.FTP_LOGIN_ERROR);
                return resultInfo;
            }
        }
        //每次数据连接之前ftp client告诉ftp server开通 一个端口来传输数据。
        ftpUtil.enterLocalPassiveMode();
        //打印当前目录
//        String s = ftpUtil.printWorkingDirectory();
        FTPFile[] ftpFiles = ftpUtil.listFiles();
        boolean b = false;
//        fileName = new String(fileName.getBytes("GBK"),"iso-8859-1");
        if (ftpFiles.length > 0){
    
            for (FTPFile ff : ftpFiles) {
    
                if (ff.getName().equals(fileName)) {
    
                    ftpUtil.enterLocalPassiveMode();
                    FileOutputStream outputStream = new FileOutputStream(new File(localPath+"\\"+fileName));
                    b = ftpUtil.retrieveFile(fileName, outputStream);
                    outputStream.close();
                }
            }
        }
        if (!b){
    
            log.error("未查找到相关文件");
            resultInfo.setResultCode(ResponseCode.FTP_RETRIEVEFILE_ERROR);
            resultInfo.setData(b);
            return resultInfo;
        }
        Boolean isLogout = ftpUtil.ftpLogout();
        if (!isLogout){
    
            log.error("FTP用户登出失败");
            resultInfo.setResultCode(ResponseCode.FTP_LOGOUT_ERROR);
            return resultInfo;
        }
        resultInfo.setResultCode(ResponseCode.SUCCESS);
        resultInfo.setData(b);
        return resultInfo;
    }
}


4.创建测试类


package com.dnydys.service;

import com.dnydys.common.baseRespone.ResultInfo;
import java.io.IOException;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

/**
 * @Classname FileUploadServiceTest
 * @Description TODO
 * @Date 2021/12/17 23:28
 * @Created by hasee
 */
@SpringBootTest
public class FileUploadServiceTest {
    

    @Autowired
    private FileUploadService fileUploadService;

    //文件上传
    @Test
    public void fileUpload() throws IOException {
    

        String localPath = "C:\\Users\\hasee\\Desktop\\DNYDIS\\testfile.txt";
        System.out.println("+++++++++++++++++++++"+localPath);

        String fileName = "testfile.txt";
        String userid = "22";
        ResultInfo resultInfo = fileUploadService.fileUpload(localPath, userid, fileName);
        System.out.println(resultInfo.getResultCode());
    }

    //文件下载
    @Test
    public void fileDownload() throws IOException {
    

        String localPath = "C:\\Users\\hasee\\Desktop\\download";
        System.out.println("+++++++++++++++++++++"+localPath);
        String fileName = "testfile.txt";

        String userid = "22";
        ResultInfo resultInfo = fileUploadService.fileDownload(localPath, userid, fileName);
        System.out.println(resultInfo.getResultCode());
    }
}


基本完工 看效果

上传

在这里插入图片描述

下载

在这里插入图片描述

文件内容

为了解决这个文件内容乱码可废了老大劲儿了
搞得我感觉喝一杯黑芝麻糊泡枸杞压压惊
在这里插入图片描述

代码下载

最后附上代码链接地址
https://github.com/DNYDYS/springboot-FTP.git

有什么问题随时可以留言或者私信
赶紧接着喝我的黑芝麻糊泡枸杞去

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_42254034/article/details/122030571

智能推荐

5个超厉害的资源搜索网站,每一款都可以让你的资源满满!_最全资源搜索引擎-程序员宅基地

文章浏览阅读1.6w次,点赞8次,收藏41次。生活中我们无时不刻不都要在网站搜索资源,但就是缺少一个趁手的资源搜索网站,如果有一个比较好的资源搜索网站可以帮助我们节省一大半时间!今天小编在这里为大家分享5款超厉害的资源搜索网站,每一款都可以让你的资源丰富精彩!网盘传奇一款最有效的网盘资源搜索网站你还在为找网站里面的资源而烦恼找不到什么合适的工具而烦恼吗?这款网站传奇网站汇聚了4853w个资源,并且它每一天都会持续更新资源;..._最全资源搜索引擎

Book类的设计(Java)_6-1 book类的设计java-程序员宅基地

文章浏览阅读4.5k次,点赞5次,收藏18次。阅读测试程序,设计一个Book类。函数接口定义:class Book{}该类有 四个私有属性 分别是 书籍名称、 价格、 作者、 出版年份,以及相应的set 与get方法;该类有一个含有四个参数的构造方法,这四个参数依次是 书籍名称、 价格、 作者、 出版年份 。裁判测试程序样例:import java.util.*;public class Main { public static void main(String[] args) { List <Book>_6-1 book类的设计java

基于微信小程序的校园导航小程序设计与实现_校园导航微信小程序系统的设计与实现-程序员宅基地

文章浏览阅读613次,点赞28次,收藏27次。相比于以前的传统手工管理方式,智能化的管理方式可以大幅降低学校的运营人员成本,实现了校园导航的标准化、制度化、程序化的管理,有效地防止了校园导航的随意管理,提高了信息的处理速度和精确度,能够及时、准确地查询和修正建筑速看等信息。课题主要采用微信小程序、SpringBoot架构技术,前端以小程序页面呈现给学生,结合后台java语言使页面更加完善,后台使用MySQL数据库进行数据存储。微信小程序主要包括学生信息、校园简介、建筑速看、系统信息等功能,从而实现智能化的管理方式,提高工作效率。

有状态和无状态登录

传统上用户登陆状态会以 Session 的形式保存在服务器上,而 Session ID 则保存在前端的 Cookie 中;而使用 JWT 以后,用户的认证信息将会以 Token 的形式保存在前端,服务器不需要保存任何的用户状态,这也就是为什么 JWT 被称为无状态登陆的原因,无状态登陆最大的优势就是完美支持分布式部署,可以使用一个 Token 发送给不同的服务器,而所有的服务器都会返回同样的结果。有状态和无状态最大的区别就是服务端会不会保存客户端的信息。

九大角度全方位对比Android、iOS开发_ios 开发角度-程序员宅基地

文章浏览阅读784次。发表于10小时前| 2674次阅读| 来源TechCrunch| 19 条评论| 作者Jon EvansiOSAndroid应用开发产品编程语言JavaObjective-C摘要:即便Android市场份额已经超过80%,对于开发者来说,使用哪一个平台做开发仍然很难选择。本文从开发环境、配置、UX设计、语言、API、网络、分享、碎片化、发布等九个方面把Android和iOS_ios 开发角度

搜索引擎的发展历史

搜索引擎的发展历史可以追溯到20世纪90年代初,随着互联网的快速发展和信息量的急剧增加,人们开始感受到了获取和管理信息的挑战。这些阶段展示了搜索引擎在技术和商业模式上的不断演进,以满足用户对信息获取的不断增长的需求。

随便推点

控制对象的特性_控制对象特性-程序员宅基地

文章浏览阅读990次。对象特性是指控制对象的输出参数和输入参数之间的相互作用规律。放大系数K描述控制对象特性的静态特性参数。它的意义是:输出量的变化量和输入量的变化量之比。时间常数T当输入量发生变化后,所引起输出量变化的快慢。(动态参数) ..._控制对象特性

FRP搭建内网穿透(亲测有效)_locyanfrp-程序员宅基地

文章浏览阅读5.7w次,点赞50次,收藏276次。FRP搭建内网穿透1.概述:frp可以通过有公网IP的的服务器将内网的主机暴露给互联网,从而实现通过外网能直接访问到内网主机;frp有服务端和客户端,服务端需要装在有公网ip的服务器上,客户端装在内网主机上。2.简单的图解:3.准备工作:1.一个域名(www.test.xyz)2.一台有公网IP的服务器(阿里云、腾讯云等都行)3.一台内网主机4.下载frp,选择适合的版本下载解压如下:我这里服务器端和客户端都放在了/usr/local/frp/目录下4.执行命令# 服务器端给执_locyanfrp

UVA 12534 - Binary Matrix 2 (网络流‘最小费用最大流’ZKW)_uva12534-程序员宅基地

文章浏览阅读687次。题目:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=93745#problem/A题意:给出r*c的01矩阵,可以翻转格子使得0表成1,1变成0,求出最小的步数使得每一行中1的个数相等,每一列中1的个数相等。思路:网络流。容量可以保证每一行和每一列的1的个数相等,费用可以算出最小步数。行向列建边,如果该格子是_uva12534

免费SSL证书_csdn alphassl免费申请-程序员宅基地

文章浏览阅读504次。1、Let's Encrypt 90天,支持泛域名2、Buypass:https://www.buypass.com/ssl/resources/go-ssl-technical-specification6个月,单域名3、AlwaysOnSLL:https://alwaysonssl.com/ 1年,单域名 可参考蜗牛(wn789)4、TrustAsia5、Alpha..._csdn alphassl免费申请

测试算法的性能(以选择排序为例)_算法性能测试-程序员宅基地

文章浏览阅读1.6k次。测试算法的性能 很多时候我们需要对算法的性能进行测试,最简单的方式是看算法在特定的数据集上的执行时间,简单的测试算法性能的函数实现见testSort()。【思想】:用clock_t计算某排序算法所需的时间,(endTime - startTime)/ CLOCKS_PER_SEC来表示执行了多少秒。【关于宏CLOCKS_PER_SEC】:以下摘自百度百科,“CLOCKS_PE_算法性能测试

Lane Detection_lanedetectionlite-程序员宅基地

文章浏览阅读1.2k次。fromhttps://towardsdatascience.com/finding-lane-lines-simple-pipeline-for-lane-detection-d02b62e7572bIdentifying lanes of the road is very common task that human driver performs. This is important ..._lanedetectionlite

推荐文章

热门文章

相关标签