SpringBoot + SFTP 實現文件上傳與下載實戰


SFTP介紹
SFTP是Secure File Transfer Protocol的縮寫,安全文件傳送協議。可以為傳輸文件提供一種安全的加密方法,語法幾乎和FTP一致。
相比於FTP,SFTP更安全,但更安全帶來副作用就是的效率比FTP要低些。
SFTP是SSH的一部分,內部是采用SSH連接,所以在以下代碼中進行文件的操作都會先cd到SFTP存放文件的根路徑下。
實戰
1. 相關依賴(基於SpringBoot)

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.54</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
</dependencies>

  


2. 相關配置

```
#============================================================================
# SFTP Client Setting
#============================================================================
# 協議
sftp.client.protocol=sftp
# ip地址
sftp.client.host=127.0.0.1
# 端口
sftp.client.port=22
# 用戶名
sftp.client.username=sftp
# 密碼
sftp.client.password=sftp
# 根路徑
sftp.client.root=/home/sftp/
# 密鑰文件路徑
sftp.client.privateKey=
# 密鑰的密碼
sftp.client.passphrase=
# 
sftp.client.sessionStrictHostKeyChecking=no
# session連接超時時間
sftp.client.sessionConnectTimeout=15000
# channel連接超時時間
sftp.client.channelConnectedTimeout=15000
```

  


這里暫時沒有使用到使用加密密鑰的方式登陸,所以暫不填寫
3. 將application.properties中配置轉為一個Bean

```
@Getter
@Setter
@Component
@ConfigurationProperties(ignoreUnknownFields = false, prefix = "sftp.client")
public class SftpProperties {
private String host;

private Integer port;

private String protocol;

private String username;

private String password;

private String root;

private String privateKey;

private String passphrase;

private String sessionStrictHostKeyChecking;

private Integer sessionConnectTimeout;

private Integer channelConnectedTimeout;
}
```

 


4. 將上傳下載文件封裝成Service
FileSystemService

```
/**
* @author jason.tang
* @create 2019-03-07 13:33
* @description
*/
public interface FileSystemService {

boolean uploadFile(String targetPath, InputStream inputStream) throws Exception;

boolean uploadFile(String targetPath, File file) throws Exception;

File downloadFile(String targetPath) throws Exception;

boolean deleteFile(String targetPath) throws Exception;
}
```

 


實現類:FileSystemServiceImpl(此處省略相關上傳下載代碼)

```
/**
* @author jason.tang
* @create 2019-03-07 13:33
* @description
*/
@Slf4j
@Service("fileSystemService")
public class FileSystemServiceImpl implements FileSystemService {

@Autowired
private SftpProperties config;

// 設置第一次登陸的時候提示,可選值:(ask | yes | no)
private static final String SESSION_CONFIG_STRICT_HOST_KEY_CHECKING = "StrictHostKeyChecking";

/**
* 創建SFTP連接
* @return
* @throws Exception
*/
private ChannelSftp createSftp() throws Exception {
JSch jsch = new JSch();
log.info("Try to connect sftp[" + config.getUsername() + "@" + config.getHost() + "], use password[" + config.getPassword() + "]");

Session session = createSession(jsch, config.getHost(), config.getUsername(), config.getPort());
session.setPassword(config.getPassword());
session.connect(config.getSessionConnectTimeout());

log.info("Session connected to {}.", config.getHost());

Channel channel = session.openChannel(config.getProtocol());
channel.connect(config.getChannelConnectedTimeout());

log.info("Channel created to {}.", config.getHost());

return (ChannelSftp) channel;
}

/**
* 加密秘鑰方式登陸
* @return
*/
private ChannelSftp connectByKey() throws Exception {
JSch jsch = new JSch();

// 設置密鑰和密碼 ,支持密鑰的方式登陸
if (StringUtils.isNotBlank(config.getPrivateKey())) {
if (StringUtils.isNotBlank(config.getPassphrase())) {
// 設置帶口令的密鑰
jsch.addIdentity(config.getPrivateKey(), config.getPassphrase());
} else {
// 設置不帶口令的密鑰
jsch.addIdentity(config.getPrivateKey());
}
}
log.info("Try to connect sftp[" + config.getUsername() + "@" + config.getHost() + "], use private key[" + config.getPrivateKey()
+ "] with passphrase[" + config.getPassphrase() + "]");

Session session = createSession(jsch, config.getHost(), config.getUsername(), config.getPort());
// 設置登陸超時時間
session.connect(config.getSessionConnectTimeout());
log.info("Session connected to " + config.getHost() + ".");

// 創建sftp通信通道
Channel channel = session.openChannel(config.getProtocol());
channel.connect(config.getChannelConnectedTimeout());
log.info("Channel created to " + config.getHost() + ".");
return (ChannelSftp) channel;
}

/**
* 創建session
* @param jsch
* @param host
* @param username
* @param port
* @return
* @throws Exception
*/
private Session createSession(JSch jsch, String host, String username, Integer port) throws Exception {
Session session = null;

if (port <= 0) {
session = jsch.getSession(username, host);
} else {
session = jsch.getSession(username, host, port);
}

if (session == null) {
throw new Exception(host + " session is null");
}

session.setConfig(SESSION_CONFIG_STRICT_HOST_KEY_CHECKING, config.getSessionStrictHostKeyChecking());
return session;
}

/**
* 關閉連接
* @param sftp
*/
private void disconnect(ChannelSftp sftp) {
try {
if (sftp != null) {
if (sftp.isConnected()) {
sftp.disconnect();
} else if (sftp.isClosed()) {
log.info("sftp is closed already");
}
if (null != sftp.getSession()) {
sftp.getSession().disconnect();
}
}
} catch (JSchException e) {
e.printStackTrace();
}
}
}
```

 


5. 上傳文件
5.1 將inputStream上傳到指定路徑下(單級或多級目錄)

```
@Override
public boolean uploadFile(String targetPath, InputStream inputStream) throws Exception {
ChannelSftp sftp = this.createSftp();
try {
sftp.cd(config.getRoot());
log.info("Change path to {}", config.getRoot());

int index = targetPath.lastIndexOf("/");
String fileDir = targetPath.substring(0, index);
String fileName = targetPath.substring(index + 1);
boolean dirs = this.createDirs(fileDir, sftp);
if (!dirs) {
log.error("Remote path error. path:{}", targetPath);
throw new Exception("Upload File failure");
}
sftp.put(inputStream, fileName);
return true;
} catch (Exception e) {
log.error("Upload file failure. TargetPath: {}", targetPath, e);
throw new Exception("Upload File failure");
} finally {
this.disconnect(sftp);
}
}
```

 


5.2 創建多級目錄

```
private boolean createDirs(String dirPath, ChannelSftp sftp) {
if (dirPath != null && !dirPath.isEmpty()
&& sftp != null) {
String[] dirs = Arrays.stream(dirPath.split("/"))
.filter(StringUtils::isNotBlank)
.toArray(String[]::new);

for (String dir : dirs) {
try {
sftp.cd(dir);
log.info("Change directory {}", dir);
} catch (Exception e) {
try {
sftp.mkdir(dir);
log.info("Create directory {}", dir);
} catch (SftpException e1) {
log.error("Create directory failure, directory:{}", dir, e1);
e1.printStackTrace();
}
try {
sftp.cd(dir);
log.info("Change directory {}", dir);
} catch (SftpException e1) {
log.error("Change directory failure, directory:{}", dir, e1);
e1.printStackTrace();
}
}
}
return true;
}
return false;
}
```

 


5.3 將文件上傳到指定目錄

```

@Override
public boolean uploadFile(String targetPath, File file) throws Exception {
return this.uploadFile(targetPath, new FileInputStream(file));
}
```

 

6. 下載文件

```
@Override
public File downloadFile(String targetPath) throws Exception {
ChannelSftp sftp = this.createSftp();
OutputStream outputStream = null;
try {
sftp.cd(config.getRoot());
log.info("Change path to {}", config.getRoot());

File file = new File(targetPath.substring(targetPath.lastIndexOf("/") + 1));

outputStream = new FileOutputStream(file);
sftp.get(targetPath, outputStream);
log.info("Download file success. TargetPath: {}", targetPath);
return file;
} catch (Exception e) {
log.error("Download file failure. TargetPath: {}", targetPath, e);
throw new Exception("Download File failure");
} finally {
if (outputStream != null) {
outputStream.close();
}
this.disconnect(sftp);
}
}
```

 


7. 刪除文件

```
/**
* 刪除文件
* @param targetPath
* @return
* @throws Exception
*/
@Override
public boolean deleteFile(String targetPath) throws Exception {
ChannelSftp sftp = null;
try {
sftp = this.createSftp();
sftp.cd(config.getRoot());
sftp.rm(targetPath);
return true;
} catch (Exception e) {
log.error("Delete file failure. TargetPath: {}", targetPath, e);
throw new Exception("Delete File failure");
} finally {
this.disconnect(sftp);
}
}
```

 

##### 8\. 最后

* 涉及到對文件的操作,一定記得將流關閉。
* 在使用中比如下載文件,請將生成的文件在使用后刪除(file.delete()),避免在服務器中占據大量資源。
* application.proerties中SFTP相關配置,請自行更換。如有不對之處,請指出,感謝閱讀!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM