Nginx代理内网上传文件到外网Sftp服务器

业务场景

# 架构

# Sftp服务器

# 物理机

  • 公网IP1:22
  • 公网IP2:22

# Nginx服务器

# 物理机

  • 内网IP:80 (可访问外网)

# 代理sftp配置

stream {

   upstream sftp1{
       hash $remote_addr consistent;
       server 公网IP1:22;
   }

   server {
       listen 7115;
       proxy_connect_timeout 300s;
       proxy_timeout 300s;
       proxy_pass sftp1;
  }

  upstream sftp2{
      hash $remote_addr consistent;
      server 公网IP2:22;
  }

  server {
      listen 7116;
      proxy_connect_timeout 300s;
      proxy_timeout 300s;
      proxy_pass sftp2;
 }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# 已安装Nginx情况下添加新的module

# 源码目录
cd /usr/local/nginx/nginx-1.14.0
# 编译
./configure --prefix=/usr/local/nginx --with-stream

make

# 停止
/usr/local/nginx/sbin/nginx -s stop
# 备份原有nginx二进制文件
cp /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx.20210818
# 复制新编译好的nginx二进制文件(此处nginx源码目录为。即为编译命令执行目录)
cp ./objs/nginx /usr/local/nginx/sbin/nginx
# 启动
/usr/local/nginx/sbin/nginx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

参考:
https://blog.csdn.net/zlxtk/article/details/80995955 (opens new window)
https://blog.csdn.net/jijiuqiu6646/article/details/78675891 (opens new window)
https://www.cnblogs.com/crysmile/p/9565048.html (opens new window)

# Java代码

@Slf4j
@Service
public class ContentMediaSftpService {

  @Value("${sftp.path.mediaPrefix:}")
  private String pathMediaPrefix;

  @Value("${sftp.path.mediaDirectory:media}")
  private String pathMediaDirectory;

  String upload(MultipartFile file, String fileName) {
    boolean result = true;
    SftpUtil sftpUtil1 = getSftp1();
    SftpUtil sftpUtil2 = getSftp2();
    // 生成临时文件
    File tempFile = FileUtil.multipartFile2File(file, "/home/ms/attachment/temp");
    try {
      sftpUtil1.login();
      sftpUtil1.uploadFile(pathMediaPrefix, pathMediaDirectory, fileName, tempFile);
      sftpUtil1.logout();
    } catch (Exception e) {
      result = false;
      log.error("sftp1文件上传失败", e);
    }
    if (!result) {
      tempFile.delete();
      return null;
    }
    try {
      sftpUtil2.login();
      sftpUtil2.uploadFile(pathMediaPrefix, pathMediaDirectory, fileName, tempFile);
      sftpUtil2.logout();
    } catch (Exception e) {
      result = false;
      log.error("sftp2文件上传失败", e);
    }
    tempFile.delete();
    if (!result) {
      return null;
    }
    return pathMediaPrefix + "/" + pathMediaDirectory + "/" + fileName;
  }

  public boolean delete(String directory, String deleteFileName) {
    checkSftpConfig(ContentMediaErrorEnum.FILE_DELETE_ERROR);
    SftpUtil sftpUtil1 = getSftp1();
    SftpUtil sftpUtil2 = getSftp2();
/*    SftpUtil sftpUtil1 = new SftpUtil("portal", "portal.0756", "172.30.21.56", 7115); // 172.30.22.143
    SftpUtil sftpUtil2 = new SftpUtil("portal", "portal.0756", "172.30.21.56", 7116); // 172.30.22.167*/
    boolean result1 = true;
    boolean result2 = true;
    try {
      sftpUtil1.login();
      sftpUtil1.delete(directory, deleteFileName);
      sftpUtil1.logout();
    } catch (Exception e) {
      result1 = false;
      log.error("sftp1文件删除失败", e);
    }
    try {
      sftpUtil2.login();
      sftpUtil2.delete(directory, deleteFileName);
      sftpUtil2.logout();
    } catch (Exception e) {
      result2 = false;
      log.error("sftp2文件删除失败", e);
    }
    ContentMediaErrorEnum.FILE_DELETE_ERROR.isFalse(result1 && result2);
    return result1 && result2;
  }

  private SftpUtil getSftp1() {
    return new SftpUtil("Sftp服务器1用户名", "Sfrp服务器1密码", "Nginx代理服务Ip", 7115);
  }

  private SftpUtil getSftp2() {
    return new SftpUtil("Sftp服务器2用户名", "Sfrp服务器2密码", "Nginx代理服务Ip", 7116);
  }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
public class FileUtil {

  private FileUtil() {
  }

  public static File multipartFile2File(MultipartFile multipartFile, String basePathName) {
    String fileType = getFileType(multipartFile.getOriginalFilename());
    String pathName = generateFileFullName(basePathName, fileType);
    File file = new File(pathName); 
    try {
      FileUtils.copyInputStreamToFile(multipartFile.getInputStream(), file);
    } catch (IOException e) {
      throw new SystemException(e);
    }
    return file;
  }

  public static String getFileType(String fileName) {
    return fileName.substring(fileName.lastIndexOf(".") + 1);
  }

  public static String generateFileFullName(String basePath, String fileType) {
    String filePath = basePath + "/";
    File file = new File(filePath);
    if (!file.exists()) {
      file.mkdirs();
    }
    String fileName = generateFileName(fileType);
    return filePath + fileName;
  }

  public static String generateFileName(String fileType) {
    StringBuilder fileNameBuilder = new StringBuilder();
    if(!fileType.startsWith(".")) {
      fileNameBuilder.append(UUID.randomUUID()).append(".").append(fileType);
    }else {
      fileNameBuilder.append(UUID.randomUUID()).append(fileType);
    }
    return fileNameBuilder.toString();
  }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
@Slf4j
public class SftpUtil {

	private ChannelSftp sftp;
	private Session session;
	/** SFTP 登录用户名 */
	private String username;
	/** SFTP 登录密码 */
	private String password;
	/** 私钥 */
	private String privateKey;
	/** SFTP 服务器地址IP地址 */
	private String host;
	/** SFTP 端口 */
	private int port;

	/**
	 * 构造基于密码认证的sftp对象
	 */
	public SftpUtil(String username, String password, String host, int port) {
		this.username = username;
		this.password = password;
		this.host = host;
		this.port = port;
	}

	/**
	 * 构造基于秘钥认证的sftp对象
	 */
	public SftpUtil(String username, String host, int port, String privateKey) {
		this.username = username;
		this.host = host;
		this.port = port;
		this.privateKey = privateKey;
	}

	public SftpUtil() {
	}

	/**
	 * 连接sftp服务器
	 */
	public void login() {
		try {
			JSch jsch = new JSch();
			if (privateKey != null) {
				// 设置私钥
				jsch.addIdentity(privateKey);
			}
			session = jsch.getSession(username, host, port);
			if (password != null) {
				session.setPassword(password);
			}
			Properties config = new Properties();
			config.put("StrictHostKeyChecking", "no");

			session.setConfig(config);
			session.connect();

			Channel channel = session.openChannel("sftp");
			channel.connect();

			sftp = (ChannelSftp) channel;
		} catch (JSchException e) {
			log.error("=====sftp login error =====", e);
		}
	}

	/**
	 * 关闭连接 server
	 */
	public void logout() {
		if (sftp != null && sftp.isConnected()) {
			sftp.disconnect();
		}
		if (session != null && session.isConnected()) {
			session.disconnect();
		}
	}

	/**
	 * 将输入流的数据上传到sftp作为文件。文件完整路径=basePath+directory
	 * 
	 * @param basePath
	 *          服务器的基础路径
	 * @param directory
	 *          上传到该目录
	 * @param sftpFileName
	 *          sftp端文件名
	 * @param input
	 *          输入流
	 */
	private void upload(String basePath, String directory, String sftpFileName, InputStream input) throws SftpException {
		try {
			sftp.cd(basePath);
			sftp.cd(directory);
		} catch (SftpException e) {
			// 目录不存在,则创建文件夹
			String[] dirs = directory.split("/");
			StringBuilder tempPath = new StringBuilder();
			tempPath.append(basePath);
			for (String dir : dirs) {
				if (null == dir || "".equals(dir)) {
					continue;
				}
				tempPath.append("/").append(dir);
				try {
					sftp.cd(tempPath.toString());
				} catch (SftpException ex) {
					sftp.mkdir(tempPath.toString());
					sftp.cd(tempPath.toString());
				}
			}
		}
		// 上传文件
		sftp.put(input, sftpFileName);
	}
	
	/**
	 * 将件数据上传到sftp 文件完整路径=basePath+directory
	 *
	 * @param basePath
	 *          服务器的基础路径
	 * @param directory
	 *          上传到该目录
	 * @param sftpFileName
	 *          sftp端文件名
	 * @param file
	 *          文件
	 */
	public void uploadFile(String basePath, String directory, String sftpFileName, File file) throws IOException,SftpException{
		try(FileInputStream fileInputStream = new FileInputStream(file)){
			upload(basePath, directory, sftpFileName, fileInputStream);
		}
	}

	/**
	 * 删除文件
	 * 
	 * @param directory
	 *          要删除文件所在目录
	 * @param deleteFile
	 *          要删除的文件
	 */
	public void delete(String directory, String deleteFile) throws SftpException {
		sftp.cd(directory);
		sftp.rm(deleteFile);
	}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150

参考: https://blog.csdn.net/wagnteng/article/details/78481836 (opens new window)