导入依赖

<dependency>
    <groupId>commons-net</groupId>
    <artifactId>commons-net</artifactId>
    <version>3.8.0</version>
</dependency>

controller层

package com.neutech.controller;

import com.neutech.vo.ResultVO;
import com.neutech.vo.WangEditorItemVO;
import com.neutech.vo.WangEditorVO;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

@RestController
@RequestMapping(value = "/upload")
public class FileUploadController {

    @Value("${ftp.hostname}")
    private String hostname;

    @Value("${ftp.port}")
    private Integer port;

    @Value("${ftp.username}")
    private String username;

    @Value("${ftp.password}")
    private String password;

    @Value("${ftp.speed}")
    private Integer speed;

    @Value("${nginx.prefix}")
    private String nginxPrefix;

    @PostMapping("/fileUpload")
    public ResultVO fileUpload(MultipartFile file) {
        // MultipartFile类型的对象当前台不传此参数有时是null,有时不是null
        if (file == null || file.getSize() == 0) {
            return ResultVO.error(1006, "文件未选择");
        }

        String fileName = "";
        FTPClient ftpClient = new FTPClient();
        try {
            ftpClient.connect(hostname, port);
            ftpClient.login(username, password);
            int replyCode = ftpClient.getReplyCode();
            if (!FTPReply.isPositiveCompletion(replyCode)) {
                return ResultVO.error(1007, "ftp服务器连接失败");
            }
            ftpClient.setControlEncoding("utf-8");
            ftpClient.enterLocalPassiveMode();
            ftpClient.setBufferSize(speed);
            ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);

            // 切换文件夹
            ftpClient.changeWorkingDirectory("/home/ftpuser/");
            // 推送到服务器上的文件名一定不能重复,需要我们生成一个不重复的文件名
            // UUID 号称全世界都不会重复 1.jpg
            fileName = UUID.randomUUID().toString() +
                    file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
            if (!ftpClient.storeFile(fileName, file.getInputStream())) {
                return ResultVO.error(1009, "上传失败");
            }

        } catch (IOException e) {
            e.printStackTrace();
            return ResultVO.error(1010, "ftp服务器连接失败");
        }
        // 生成一个可访问的地址返回
        return ResultVO.success(nginxPrefix + fileName);
//        return ResultVO.success(fileName);
    }


    @PostMapping("/wangUpload")
    public WangEditorVO wangUpload(MultipartFile[] files) {
        if (files == null || files.length == 0) {
            return WangEditorVO.error();
        }

        List<WangEditorItemVO> wangEditorItemVOList = new ArrayList<>();
        for (MultipartFile file : files) {
            ResultVO resultVO = fileUpload(file);
            WangEditorItemVO wangEditorItemVO = new WangEditorItemVO();
            wangEditorItemVO.setUrl((String) resultVO.getData());
            wangEditorItemVOList.add(wangEditorItemVO);
        }

        return WangEditorVO.success(wangEditorItemVOList);
    }
}

配置文件层

application.yml文件中写入:

ftp:
  hostname: 192.168.253.151
  port: 21
  username: ftpuser
  password: 52xzg1314
  speed: 1024
nginx:
  prefix: http://192.168.253.151/

说明:

1. 解决更换端口号时需要代码重新编译打包的问题
  1. 一些值放常量类里:需要重启服务器、重新编译、重新打包
  2. 可以防止配置文件中:项目重启,但不用重新编译。配置文件不是java代码,不用重新编译。

这里使用第二种方式:
首先,yml配置文件中写入如下内容。

ftp:
  hostname: 192.168.253.151
  post: 21
  username: ftpuser
  password: 52xzg1314
  • 注意:yml文件中,数字不可以以0开头,会把他当成八进制解析。
    解决方法:
    加双引号,告诉他这是字符串,你别转

而在java代码中读取配置文件:

  • (在添加了@RestController注解的类里面,涉及框架原理:
    yml文件是spring里面的。
    当添加了@RestController表示将这个类交给IOC去处理,spring就能往类里面添加值。
    而不添加时,spring无法往类里面加东西
@Value("${ftp.hostname}")
    private String hostname;

    @Value("${ftp.port}")
    private Integer port;

    @Value("${ftp.username}")
    private String username;

    @Value("${ftp.password}")
    private String password;

    @Value("${nginx.prefix}")
    private String nginxPrefix;
2. 保证逻辑完整

保证在任何情况下都有返回值

3. 文件名处理

不可以叫用户自己的文件名。容易重名。
如何生成一个不重复的文件名:

  1. 时间:不行(web是并发程序)
  2. UUID号称全世界都不会重复
fileName = UUID.randomUUID().toString() +
        file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
if (!ftpClient.storeFile(fileName, file.getInputStream())) {
    return ResultVO.error(1009, "上传失败");
}

UUID:36位,无后缀

结果示例:

postman请求入下:

springboot 连接rtmp服务 springboot连接ftp_springboot 连接rtmp服务


Xftp中也可以看到该文件

springboot 连接rtmp服务 springboot连接ftp_nginx_02

linux下nginx配置文件访问路径

whereis nginx
cd xxxx
ll
cd conf
vi nginx.conf

springboot 连接rtmp服务 springboot连接ftp_spring_03

更改配置文件到图片存储路径:如图:

springboot 连接rtmp服务 springboot连接ftp_spring boot_04

浏览器访问

启动nginx:

systemctl start nginx

nginx403原因:用户权限问题
解决:

  1. 传到一个nginx能访问的路径,ftpuser用户就得配权限
  2. nginx能访问ftpuser目录

采用方法二:

如下图:更改这一行语句如图:

springboot 连接rtmp服务 springboot连接ftp_spring_05


之后访问:

linux主机ip地址/图片名

例如:http://192.168.253.151/a08f4ff4-ebac-47d0-9433-d78c4a7020c8.jpg访问成功。

springboot 连接rtmp服务 springboot连接ftp_spring boot_06

后续运行

只需要在虚拟机内启动ftp服务后,即可发送文件。

systemctl start vsftpd