如何通过中转SFTP服务器C将服务器A生成的文件上传至白名单服务器B?
Alright, let's tackle this problem where you need to route a file from Java app server A to server B (via whitelisted SFTP path /home/fixed/file.xlsx) using an intermediate server C that you already have credentials for. Here are two practical, production-ready approaches:
Approach 1: Two-Stage Transfer (A → C → B)
This is the most straightforward method, especially if you don't want to deal with SSH tunneling. It involves transferring the file to a temporary location on C first, then moving it to B from there.
Step-by-Step Breakdown
Upload from A to C's temporary directory
Use a Java SFTP library like JSch or Apache Commons VFS to push the generated file to a secure temp path on C (e.g.,/tmp/sftp_transfers/). Make sure the temp directory has restricted permissions (only your user can read/write) to avoid security risks.Example JSch code snippet for A → C:
import com.jcraft.jsch.*; public class SftpUploadToC { public static void main(String[] args) { String hostC = "server-c-ip"; String userC = "your-c-username"; String passwordC = "your-c-password"; // Or use key-based auth for better security String localFilePath = "/path/to/generated/file.xlsx"; String remoteTempPath = "/tmp/sftp_transfers/file.xlsx"; JSch jsch = new JSch(); Session session = null; ChannelSftp channel = null; try { session = jsch.getSession(userC, hostC, 22); session.setPassword(passwordC); session.setConfig("StrictHostKeyChecking", "no"); // Disable for testing; use known hosts in production session.connect(); channel = (ChannelSftp) session.openChannel("sftp"); channel.connect(); channel.put(localFilePath, remoteTempPath); System.out.println("File uploaded to C's temp path successfully"); } catch (JSchException | SftpException e) { e.printStackTrace(); // Add error handling/alerts here (e.g., notify on failure) } finally { if (channel != null) channel.disconnect(); if (session != null) session.disconnect(); } } }Transfer from C to B
Once the file is on C, you have a few options to move it to B:- Shell Script on C: Create a bash script that uses the
sftpcommand to push the file to B's fixed path. You can trigger this script from A via SSH, or set up a cron job on C to watch the temp directory.
Example script (transfer_to_b.sh):#!/bin/bash SFTP_USER_B="your-b-username" SFTP_PASS_B="your-b-password" # Replace with key-based auth in production SFTP_HOST_B="server-b-ip" REMOTE_TEMP_PATH="/tmp/sftp_transfers/file.xlsx" REMOTE_FIXED_PATH="/home/fixed/file.xlsx" # Use batch mode to avoid interactive prompts echo -e "put $REMOTE_TEMP_PATH $REMOTE_FIXED_PATH\nexit" | sftp -o StrictHostKeyChecking=no $SFTP_USER_B@$SFTP_HOST_B # Clean up temp file after successful transfer if [ $? -eq 0 ]; then rm $REMOTE_TEMP_PATH echo "File transferred to B and temp file cleaned up" else echo "Transfer to B failed" exit 1 fi - Java Program on C: If C has a Java runtime, you can run a similar JSch-based program to handle the C→B transfer directly.
- Shell Script on C: Create a bash script that uses the
Trigger the C→B Transfer
From server A, execute the script on C via SSH using JSch'sChannelExec:// Add this to A's code right after uploading the file to C ChannelExec execChannel = (ChannelExec) session.openChannel("exec"); execChannel.setCommand("bash /path/to/transfer_to_b.sh"); execChannel.connect(); // Read script output/errors to confirm success InputStream in = execChannel.getInputStream(); byte[] buffer = new byte[1024]; while (in.read(buffer) != -1) { System.out.print(new String(buffer)); } execChannel.disconnect();
Approach 2: SSH Tunneling (A → C → B Directly)
This method avoids storing the file on C entirely by using C as an SSH jump host. You'll create a port forwarding tunnel from A to B via C, then upload the file directly to the forwarded port (which maps to B's SFTP service).
Step-by-Step Breakdown
- Establish SSH Tunnel from A to B via C
In Java, use JSch to create a local port forward:import com.jcraft.jsch.*; public class SftpTunnelToB { public static void main(String[] args) { String hostC = "server-c-ip"; String userC = "your-c-username"; String passwordC = "your-c-password"; String hostB = "server-b-ip"; int localPort = 2222; // Local port on A to forward to B's SFTP port (22) int remotePortB = 22; JSch jsch = new JSch(); Session sessionC = null; try { sessionC = jsch.getSession(userC, hostC, 22); sessionC.setPassword(passwordC); sessionC.setConfig("StrictHostKeyChecking", "no"); sessionC.connect(); // Set up port forwarding: localhost:2222 → B:22 via C sessionC.setPortForwardingL(localPort, hostB, remotePortB); System.out.println("SSH tunnel established: localhost:" + localPort + " → " + hostB + ":" + remotePortB); // Upload file to localhost:2222 (which routes to B's SFTP) uploadToSftp("localhost", localPort, "your-b-username", "your-b-password", "/path/to/generated/file.xlsx", "/home/fixed/file.xlsx"); } catch (JSchException e) { e.printStackTrace(); } finally { if (sessionC != null) sessionC.disconnect(); } } private static void uploadToSftp(String host, int port, String user, String pass, String localPath, String remotePath) throws JSchException, SftpException { JSch jsch = new JSch(); Session session = jsch.getSession(user, host, port); session.setPassword(pass); session.setConfig("StrictHostKeyChecking", "no"); session.connect(); ChannelSftp channel = (ChannelSftp) session.openChannel("sftp"); channel.connect(); channel.put(localPath, remotePath); channel.disconnect(); session.disconnect(); } }
Key Considerations for Both Approaches
- Security: Always prefer SSH key-based authentication over passwords for all SFTP/SSH connections. Restrict file permissions on temp directories (e.g.,
chmod 700for directories,chmod 600for files) to prevent unauthorized access. - Error Handling: Implement retry logic for failed transfers, add detailed logging for each step, and set up alerts (e.g., email, team chat) for transfer failures.
- Whitelisting: Ensure server C's IP is included in B's SFTP whitelist, otherwise the C→B transfer will be blocked.
- Cleanup: For Approach 1, always clean up temp files on C after successful transfers to avoid unnecessary disk usage.
内容的提问来源于stack exchange,提问作者Black Diamond

