上传遇到这种情况,去FTP服务器上看看,如果文件也确实存在,只不过内容是空的。最简单的办法就是试试关闭服务器防火墙(直接在服务器上敲命令或手动关闭即可),如果关闭防火墙,文件能正常上传,那就好办了。ftp默认的上传模式是主动上传,如果防火墙不能关闭,在代码里上传模式改成被动上传就行。

先看代码:

public Result uploadFile(@RequestParam(value = "file") MultipartFile file) {
        try {
            String fileName = file.getOriginalFilename();
            String suffixName = fileName.substring(fileName.indexOf(".") + 1);
            Map<Object, Object> map = new HashMap<>();
            map.put("filePath", FileStoreUtil.uploadFile(file.getBytes(), suffixName, false, "文件名"));
            return Result.success(map);
        } catch (Exception e) {
            e.printStackTrace();
            return Result.error("文件上传失败,失败原因:" + e.getMessage());
        }
    }

 这里因为上传服务要做兼容,config对象的子类为oss和ftp两个对象。

/** 
  * @param fileContent byte[]文件字节数组对象
  * @param suffixName  文件名后缀名
  * @param fileName  文件前缀名
  * @param isTemp  是否加入临时目录标识
  */
public static String uploadFile(byte[] fileContent, String suffixName, boolean isTemp,String fileName) throws Exception {
        StringBuilder str = new StringBuilder();
        if (isTemp) {
            str.append("temp").append("/");
        }
        //如果有业务号或者id也可以拼进去
        if (StringUtil.isNotEmpty(fileName)) {
            str.append(fileName).append(".").append(suffixName);
        }else{//拼接日期和随机数
            str.append(DateUtil.getYear()).append("/")
                  .append(DateUtil.getMonth()).append("/")
                  .append(DateUtil.getDay()).append("/")
                  .append(System.currentTimeMillis())
                  .append(new Random().nextInt(999))
                  .append(".").append(suffixName);
        }
        String name = str.toString();
        //判断下用什么服务器做文件管理
        Config config;
        if ("ftp".equals(GlobalParamConfig.FILE_STORY_WAY)) {
            String model = GlobalParamConfig.FILE_FTP_MODEL;//这里是从nacos获取模式配置,也可以用字典等等。
            if(DictDefinition.FILE_FTP_PASSIVE_MODE.equals(model)){//被动模式
                config = new FtpConfig("host", "ip", "username", "password", model);
            }else {//默认为主动模式
                config = new FtpConfig("host", "ip", "username", "password", null);
            }
        } else if ("oss".equals(GlobalParamConfig.FILE_STORY_WAY)) {//用oss阿里云
            config = new OSSConfig("url", "accesskeyid","bucket","accesskeysecret","isencrypt");
        } else {
            throw new CrmException("文件上传与下载方式,服务配置错误");
        }
        FileStore fileStore = new FtpUtil(config);
        boolean storeFile = fileStore.storeFile(name, new ByteArrayInputStream(fileContent));
        return name;
    }

这里创建ftp的客户端,做一些常规设置,然后判断是否把上传模式改为被动模式

public FtpUtil(String ip, int port, String userName, String password, String model) {
        try {
            //创建FTPClient连接,并登录
            this.ftpClient = new FTPClient();
            this.ftpClient.connect(ip, port);
            this.ftpClient.setControlEncoding("GBK");
            FTPClientConfig config = new FTPClientConfig("WINDOWS");
            config.setServerLanguageCode("zh");
            this.ftpClient.login(userName, password);
            if (model != null && !"".equals(model)) {//判断设置被动模式
                this.ftpClient.enterLocalPassiveMode();
            }
            //reply是连接后,ftp给的整数值回复码,判断回复码是否正常
            int reply = this.ftpClient.getReplyCode();
            if (!FTPReply.isPositiveCompletion(reply)) {
                this.ftpClient.disconnect();
                throw new Exception("连接ftp服务器出错,请联系管理员处理!");
            }
        } catch (Exception var8) {
            System.err.println("%%%% Error Creating FTPConnection %%%%");
            log.severe("连接ftp服务器出错!");
            var8.printStackTrace();
        }

    }

后面就是做上传文件操作了。这里有两种方法实现,我这边用的第二个方法。

public boolean storeFile(String path, InputStream inputStream) {
        boolean success = false;
        String fileType = FileUtils.getFileType(path);
        String tempFile = FileUtils.creteTempPath(fileType);
        try {
            FileUtils.writeBytesToFile(input2byte(inputStream), tempFile);
            byte[] encrypt = AES.encrypt(FileUtils.readBytesFromFile(new File(tempFile)));
            FileUtils.writeBytesToFile(encrypt, tempFile);
            success = this.ftpUtil.upload(new FileInputStream(new File(tempFile)), path);
        } catch (Exception var7) {
            var7.printStackTrace();
        }

        FileUtils.deleteFile(tempFile);
        this.ftpUtil.disConFtp();
        return success;
    }


public boolean storeFile(String path, InputStream inputStream) {
    boolean success = ftpUtil.upload(inputStream, path);
    //关闭ftp连接
    ftpUtil.disConFtp();
    return success;
}

通过upload把文件上传到ftp服务器上。

public boolean upload(InputStream fis, String targetFile) {
        targetFile = "/" + targetFile;
        String targetPath = targetFile.substring(0, targetFile.lastIndexOf("/"));
        String fileName = targetFile.substring(targetFile.lastIndexOf("/") + 1);
        boolean success = false;

        try {
            this.mkDir(targetPath);
            this.ftpClient.setFileType(2);
            success = this.ftpClient.storeFile(fileName, fis);//FTPClient自带的上传文件方法
        } catch (IOException var10) {
            var10.printStackTrace();
            log.severe("上传出错,请重新上传");
        } finally {
            IOUtils.closeQuietly(fis);
        }
        //因为调用upload的方法,后面有关闭ftp连接,这里直接返回就可以了
        return success;
    }


//完成操作,断开连接
 public void disConFtp() {
        try {
            if (null != this.ftpClient) {
                this.ftpClient.logout();
                if (this.ftpClient.isConnected()) {
                    this.ftpClient.disconnect();
                }
            }
        } catch (IOException var2) {
            log.severe("退出ftp异常,检查网络!");
            var2.printStackTrace();
        }

    }

//判断文件路径,切换到该路径
private synchronized void mkDir(String filePath) throws IOException {
        String[] fileName = filePath.split("/");

        for(int i = 0; i < fileName.length; ++i) {
            String targetPath = fileName[i];
            if (targetPath != null && !"".equals(targetPath)) {
                //判断路径不为空,就切换到该目录下
                boolean temp = this.ftpClient.changeWorkingDirectory(targetPath);
                if (!temp) {
                    //目录不存在就创建,然后切换到该目录下
                    this.ftpClient.makeDirectory(targetPath);
                    this.ftpClient.changeWorkingDirectory(targetPath);
                }
            }
        }

    }

这差不多快是全部的ftp上传代码了,最后说下这个主动和被动上传吧。

主动上传:对FTP服务器的管理有利,但对客户端的管理不利。因为FTP服务器企图与客户端的高位随机端口建立连接,而这个端口很有可能被客户端的防火墙阻塞掉。 

被动上传:对FTP客户端的管理有利,但对服务器端的管理不利。因为客户端要与服务器端建立两个连接,其中一个连到一个高位随机端口,而这个端口很有可能被服务器端的防火墙阻塞掉。

既然FTP服务器的管理员需要他们的服务器有最多的客户连接,那么必须得支持被动FTP。我们可以通过为FTP服务器指定一个有限的端口范围来减小服务器高位端口的暴露。这样,不在这个范围的任何端口会被服务器的防火墙阻塞。虽然这没有消除所有针对服务器的危险,但它大大减少了危险。 

主动或被动模式都是有利有弊,需要考虑相应的场景采用适合的模式。一般内网用被动模式 ,外网连接时用主动模式,服务器相应改动,使用应当参考实际应用场景。