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

       文件上传/下载服务是每个系统都离不开的,最近写了一个简单、通用、好用的文件上传/下载的服务。这里我们使用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 password;

        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());
        }
    }
}

总结:

       代码比较简洁,结构比较清晰,没有多余的功能,但是也没有提供比较高级的特性。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值