您的位置:首页 > 编程语言 > PHP开发

基于SFTP的文件上传下载服务

2017-10-24 17:27 441 查看
       文件上传/下载服务是每个系统都离不开的,最近写了一个简单、通用、好用的文件上传/下载的服务。这里我们使用JSch来实现文件的上传和下载,开发框架采用的是SpringBoot+Swagger。

       首先我们建立一个SFTP的工具类用来对服务器上的文件进行操作。

package org.jack.util;

import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpATTRS;
import com.jcraft.jsch.SftpException;
import org.apache.log4j.Logger;

import java.io.InputStream;
import java.io.OutputStream;
import java.util.Properties;

/**
* @author jack
*/
public class SFTPUtil {

private static final Logger logger = Logger.getLogger(SFTPUtil.class);

/**
* 上传文件
*
* @param dataSource  连接信息
* @param directory   文件目录
* @param fileName    文件名
* @param inputStream 输入流
*/
public static void upload(DataSource dataSource, String directory, String fileName, InputStream inputStream) throws SftpException, JSchException {
Session session = null;
ChannelSftp sftp = null;
try {
session = connect(dataSource);
sftp = (ChannelSftp) session.openChannel("sftp");
sftp.connect();
logger.info("Channel opened.");
put(sftp, directory, fileName, inputStream);
} catch (SftpException sftpException) {
logger.error("上传文件失败", sftpException);
throw sftpException;
} catch (JSchException jSchException) {
logger.error("上传文件失败", jSchException);
throw jSchException;
} finally {
disconnect(sftp);
disconnect(session);
}

}

/**
* 下载文件
*
* @param dataSource   连接信息
* @param directory    文件目录
* @param fileName     文件名
* @param outputStream 输出流
*/
public static void download(DataSource dataSource, String directory, String fileName, OutputStream outputStream) throws SftpException, JSchException {
Session session = null;
ChannelSftp sftp = null;
try {
session = connect(dataSource);
sftp = (ChannelSftp) session.openChannel("sftp");
sftp.connect();
logger.info("Channel opened.");
get(sftp, directory, fileName, outputStream);
} catch (SftpException sftpException) {
logger.error("下载文件失败", sftpException);
throw sftpException;
} catch (JSchException jSchException) {
logger.error("下载文件失败", jSchException);
throw jSchException;
} finally {
disconnect(sftp);
disconnect(session);
}
}

/**
* 删除文件
*
* @param dataSource 连接信息
* @param directory  文件目录
* @param fileName   文件名
*/
public static void delete(DataSource dataSource, String directory, String fileName) throws SftpException, JSchException {
Session session = null;
ChannelSftp sftp = null;
try {
session = connect(dataSource);
sftp = (ChannelSftp) session.openChannel("sftp");
sftp.connect();
logger.info("Channel opened.");
rm(sftp, directory, fileName);
} catch (SftpException sftpException) {
logger.error("删除文件失败", sftpException);
throw sftpException;
} catch (JSchException jSchException) {
logger.error("删除文件失败", jSchException);
throw jSchException;
} finally {
disconnect(sftp);
disconnect(session);
}
}

/* 连接Session */
private static Session connect(DataSource dataSource) throws JSchException {
JSch jSch = new JSch();
Session session = jSch.getSession(dataSource.getUsername(), dataSource.getHost(), dataSource.getPort());
logger.info("Session created.");
session.setPassword(dataSource.getPassword());
Properties config = new Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.setTimeout(300_000);
session.connect();
logger.info("Session connected.");
return session;
}

/* 关闭Session */
private static void disconnect(Session session) {
if (session != null && session.isConnected()) {
session.disconnect();
logger.info("session closed.");
}
}

/* 关闭sftp通道 */
private static void disconnect(ChannelSftp sftp) {
if (sftp != null && sftp.isConnected()) {
sftp.disconnect();
logger.info("sftp closed.");
}
}

/* 上传文件 */
private static void put(ChannelSftp sftp, String directory, String fileName, InputStream inputStream) throws SftpException {
try {// 切换目录
sftp.cd(directory);
logger.info("run cd directory.");
} catch (SftpException e) {
try {
mkdir(sftp, directory);
sftp.cd(directory);
logger.info("run mkdir directory.");
logger.info("run cd directory.");
} catch (SftpException sftpException) {
logger.error("上传文件失败", sftpException);
throw sftpException;
}
}
try {
sftp.put(inputStream, fileName);
logger.info("run put file.");
} catch (SftpException sftpException) {
logger.error("上传文件失败", sftpException);
throw sftpException;
}
}

/* 下载文件 */
private static void get(ChannelSftp sftp, String directory, String fileName, OutputStream outputStream) throws SftpException {
try {
sftp.cd(directory);
logger.info("run cd directory.");
sftp.get(fileName, outputStream);
logger.info("run get file.");
} catch (SftpException sftpException) {
logger.error("下载文件失败", sftpException);
throw sftpException;
}
}

/* 删除文件 */
private static void rm(ChannelSftp sftp, String directory, String fileName) throws SftpException {
try {
sftp.cd(directory);
logger.info("run cd directory.");
sftp.rm(fileName);
logger.info("run rm file.");
} catch (SftpException sftpException) {
logger.error("删除文件失败", sftpException);
throw sftpException;
}
}

private static void mkdir(ChannelSftp sftp, String directory) throws SftpException {
try {
if (isDirExist(sftp, directory)) {
return;
}
String pathArray[] = directory.split("/");
StringBuffer filePath = new StringBuffer("/");
for (String path : pathArray) {
if (path.equals("")) {
continue;
}
filePath.append(path + "/");
if (!isDirExist(sftp, filePath.toString())) {
sftp.mkdir(filePath.toString());
}
}
} catch (SftpException sftpException) {
throw sftpException;
}
}

private static boolean isDirExist(ChannelSftp sftp, String directory) {
boolean isDirExistFlag = false;
try {
SftpATTRS sftpATTRS = sftp.lstat(directory);
isDirExistFlag = true;
return sftpATTRS.isDir();
} catch (Exception e) {
if (e.getMessage().toLowerCase().equals("no such file")) {
isDirExistFlag = false;
}
}
return isDirExistFlag;
}

public static class DataSource {

private String host;

private int port;

private String username;

private String pass
4000
word;

public DataSource(String host, int port, String username, String password) {
this.host = host;
this.port = port;
this.username = username;
this.password = password;
}

public String getHost() {
return host;
}

public void setHost(String host) {
this.host = host;
}

public int getPort() {
return port;
}

public void setPort(int port) {
this.port = port;
}

public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}
}

}


       然后,我们定义一个业务类,对文件管理的服务进行信息更深层次的封装,以便系统的其他地方能够更简便的使用它。

package org.jack.service;

import org.jack.common.CustomerException;
import org.jack.common.HttpStatus;
import com.eastone.lease.util.SFTPUtil;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.SftpException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;

/**
* @author jack
*/
@Service
public class FileService {

@Value("${sftp.host}")
public String host;

@Value("${sftp.port}")
public int port;

@Value("${sftp.username}")
public String username;

@Value("${sftp.password}")
public String password;

/**
* 上传文件
*/
public String upload(String directory, String fileName, InputStream inputStream) {
SFTPUtil.DataSource dataSource = new SFTPUtil.DataSource(host, port, username, password);
String[] fileNameArray = fileName.split("\\.");
String suffixName = fileNameArray[fileNameArray.length - 1];
fileName = UUID.randomUUID().toString().replaceAll("-", "") + "." + suffixName;
try {
SFTPUtil.upload(dataSource, directory, fileName, inputStream);
} catch (SftpException e) {
throw new CustomerException(HttpStatus.BAD);
} catch (JSchException e) {
throw new CustomerException(HttpStatus.BAD);
}
return fileName;
}

/**
* 下载文件
*/
public void download(String directory, String fileName, OutputStream outputStream) {
SFTPUtil.DataSource dataSource = new SFTPUtil.DataSource(host, port, username, password);
try {
SFTPUtil.download(dataSource, directory, fileName, outputStream);
} catch (SftpException e) {
throw new CustomerException(HttpStatus.BAD);
} catch (JSchException e) {
throw new CustomerException(HttpStatus.BAD);
}
}
}
       最后,我们对外提供一个REST风格的API。

package org.jack.controller.sys;

import org.jack.common.JsonMessage;
import org.jack.service.FileService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

/**
* @author jack
*/
@RestController
@RequestMapping("/api/file")
@Api(tags = "file", description = "文件API")
public class FileController {

@Value("${sftp.path}")
public String directoryPrefix;

private final static Map<String, String> directoryMap = new HashMap<>();

static {
directoryMap.put("license", "license");
directoryMap.put("parking", "parking");
}

@Autowired
private HttpServletResponse response;

@Autowired
private FileService fileService;

@RequestMapping(value = "/{directory}", method = RequestMethod.POST)
@ApiOperation("上传文件")
@ApiResponses({
@ApiResponse(code = 200, response = String.class, message = "文件路径"),
})
public JsonMessage upload(@ApiParam(value = "目录", required = true) @PathVariable(value = "directory") String directory,
@ApiParam(value = "文件", allowMultiple = true, required = true) @RequestParam MultipartFile file) throws IOException {
if (!directoryMap.containsKey(directory)) {
return JsonMessage.failed("文件上传失败");
}
String fileName = file.getOriginalFilename();
System.out.println("文件名:" + fileName);
InputStream inputStream = file.getInputStream();
fileName = directoryMap.get(directory) + "/" + fileService.upload(directoryPrefix + directoryMap.get(directory), fileName, inputStream);
return JsonMessage.successed(fileName, "文件上传成功");
}

@RequestMapping(value = "/{directory}/{fileName}.{suffix}", method = RequestMethod.GET)
@ApiOperation("获取文件")
@ApiResponses({
@ApiResponse(code = 200, response = String.class, message = "下载成功"),
})
public void download(@ApiParam(value = "目录", required = true) @PathVariable(value = "directory") String directory,
@ApiParam(value = "文件名", required = true) @PathVariable(value = "fileName") String fileName,
@ApiParam(value = "后缀", required = true) @PathVariable(value = "suffix") String suffix) {
try {
fileService.download(directoryPrefix + directory, fileName + "." + suffix, response.getOutputStream());
} catch (Exception e) {
response.setStatus(HttpStatus.NOT_FOUND.value());
}
}
}

总结:

       代码比较简洁,结构比较清晰,没有多余的功能,但是也没有提供比较高级的特性。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java