SFTP实现文件上传

367 阅读4分钟

基本介绍

我们项目当中很多都需要使用sftp来实现文件上传功能。

步骤

一、引入依赖

<dependency>
    <groupId>com.jcraft</groupId>
    <artifactId>jsch</artifactId>
    <version>0.1.53</version>
</dependency>

二、编写工具类

编写实现sftp建立连接和关闭连接的工具类,简单代码如下:

/**
 * @author: huangshuai
 * @Description:用于sftp工具类
 * @date 2021/12/3 15:58
 */
@Data
@ToString
@Component
@ConfigurationProperties(prefix = "sftp")
public class SftpClient {

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

    /**
     * 主机地址
     */
    private String host;

    /**
     * 主机端口号
     */
    private int port;

    /**
     * 用户名
     */
    private String username;

    /**
     * 密码
     */
    private String password;

    /**
     * 文件路径
     */
    private String basePath;

    /**
     * 超时时间
     */
    private int timeout;

    private ChannelSftp channel;

    private Session session;

    /**
     * 建立sftp连接
     */
    public ChannelSftp openConnect() {
        try {
            JSch jsch = new JSch();
            //根据用户名,主机ip,端口获取一个Session对象
            session = jsch.getSession(username, host, port);
            //设置密码
            session.setPassword(password);
            //设置timeout时间
            session.setTimeout(timeout);
            Properties properties = new Properties();
            properties.put("StrictHostKeyChecking", "no");
            session.setConfig(properties);
            //通过Session建立链接
            session.connect();
            //打开SFTP通道
            channel = (ChannelSftp) session.openChannel("sftp");
            logger.info("开始连接sftp服务器----------------->");
            //建立SFTP通道的连接
            channel.connect();
            logger.info("连接sftp服务器成功----------------->");
        } catch (Exception e) {
            e.printStackTrace();
            logger.info("连接sftp服务器失败!");
        }
        return channel;
    }

    /**
     * 断开SFTP Channel、Session连接
     */
    public void closeConnect() {
        try {
            logger.info("开始断开sftp服务器----------------->");
            if (channel != null) {
                channel.disconnect();
            }
            if (session != null) {
                session.disconnect();
            }
            logger.info("已断开sftp服务器连接--------------->");
        } catch (Exception e) {
            logger.info("断开sftp服务器连接失败!");
        }
    }
}

三、配置SFTP连接信息

因为项目普遍都是SpringBoot项目,所以可以直接在application.properties配置配置文件中配置sftp连接信息,如下:

##连接主机IP地址
sftp.host=xxx
##端口号
sftp.port=22
## 用户名
sftp.username=xxx
## 密码
sftp.password=xxx
## 文件上传到哪个目录下
sftp.basePath=/data/POD
## 连接超时时间
sftp.timeout=60000

四、上传文件业务代码

简单的实现文件上传代码如下:

@Service
public class MoveFileService {

    private final static Logger log = LoggerFactory.getLogger(MoveFileService.class);

    private final static String FILE_TYPE = ".pdf";

    @Autowired
    private SftpClient sftpClient;

    @Autowired
    private BackSignDetailDao backSignDetailDao;

    @Value("${dalianda_down_file_prefix_url}")
    private String downLoadUrl;

    /**
     * 移动指定格式的文件
     */
    public void upLoadPatternFile() {
        ChannelSftp sftp;
        Map<String, String> headers = new HashMap<>();
        ByteArrayOutputStream bos = null;
        try {
            //查询未上传过的文件信息
            List<BackSignDetail> backSignDetailList = backSignDetailDao.qryBackSignDetailListByCond();
            if (backSignDetailList != null && backSignDetailList.size() > 0) {
                sftp = sftpClient.openConnect();
                for (BackSignDetail backSignDetail : backSignDetailList) {
                    bos = new ByteArrayOutputStream();
                    if (StringUtils.hasText(backSignDetail.getFileName())
                            && backSignDetail.getFileName().contains(".")
                            && StringUtils.hasText(backSignDetail.getFileUrl())) {
                        String fileName = backSignDetail.getFileName();
                        log.info("当前处理的文件名:{}", fileName);
                        //从另一个地方下载需要上传的文件,转成输出流
                        HttpClientUtil.downloadFile(downLoadUrl + backSignDetail.getFileUrl(), bos, headers);
                        String curTime = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
                        //上传文件命名:DoNo_yyyyMMddHHmmss.pdf
                        String newFileName = fileName.substring(0, fileName.indexOf(".")) + "_" + curTime + FILE_TYPE;
                        if (bos != null) {
                            //切换到上传文件目录
                            sftp.cd(sftpClient.getBasePath());
                            //上传文件
                            sftp.put(new ByteArrayInputStream(bos.toByteArray()), newFileName);//上传文件
                            log.info("上传文件成功,文件名:{}", newFileName);
                            if (backSignDetailDao.updateBackSignDetailById(backSignDetail.getBackSignDetailOID())) {
                                log.info("更新backSignDetailOID:{}为已经上传过文件", backSignDetail.getBackSignDetailOID());
                            }
                        }
                    }
                }
                sftpClient.closeConnect();
            }
        } catch (Exception e) {
            log.info("处理文件异常!");
            e.printStackTrace();
        } finally {
            try {
                if (bos != null) {
                    bos.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

@Slf4j
@SuppressWarnings("deprecation")
public class HttpClientUtil {

    private final static RequestConfig defaultReqConfig;

    private final static CloseableHttpClient defaultClient;

    private final static CloseableHttpClient defaultSSLClient;

    static {
        defaultReqConfig = RequestConfig.custom()
                .setConnectionRequestTimeout(10000)
                .setConnectTimeout(10000)
                .setSocketTimeout(30000) // 不能设为0,否则超时要等很久
                .setCookieSpec(CookieSpecs.STANDARD)
                .build();

        defaultClient = HttpClients.createDefault();
        defaultSSLClient = getSSLClientDefault();
    }

    private static void config(HttpRequestBase httpRequestBase) {
        // 配置请求的超时设置
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectionRequestTimeout(1000)
                .setConnectTimeout(10000)
                .setSocketTimeout(0)
                .build();
        httpRequestBase.setConfig(requestConfig);
    }

    /**
     * 绕过https安全验证
     *
     * @return
     */
    private static CloseableHttpClient getSSLClientDefault() {
        try {
            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
                //信任所有
                public boolean isTrusted(X509Certificate[] chain, String authType) {
                    return true;
                }
            }).build();
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext);
            return HttpClients.custom().setSSLSocketFactory(sslsf).build();
        } catch (Exception e) {
            throw new RuntimeException("获取HTTPS client 出错", e);
        }
    }

    public static CloseableHttpClient getHttpClient(String url) {
        if (url.startsWith("https")) {
            return defaultSSLClient;
        } else {
            return defaultClient;
        }
    }

    private static HttpGet createHttpGet(String url, Map<String, String> headers) {
        HttpGet get = new HttpGet(url);
        if (headers != null) {
            for (Map.Entry<String, String> header : headers.entrySet()) {
                get.setHeader(header.getKey(), header.getValue());
            }
        }
        return get;
    }

    /**
     * 通过HTTP的Get请求下载文件,如下载PDF文件
     * @param url 请求url
     * @param out 文件输出流
     * @param headers HTTP请求头
     * @return 下载文件的大小
     */
    public static int downloadFile(String url, OutputStream out, Map<String, String> headers) {
        CloseableHttpClient client = getHttpClient(url);
        HttpGet httpget = createHttpGet(url, headers);
        httpget.setConfig(defaultReqConfig); // 设置超时时间

        int byteCount = 0;
        try (CloseableHttpResponse response = client.execute(httpget)) {
            HttpEntity entity = response.getEntity();
            InputStream is = entity.getContent();
            byteCount = StreamUtils.copy(is, out);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return byteCount;
    }

    /**
     * POST请求URL获取内容+请求头
     * @param url
     * @return
     */
    public static String postAndHeader(String url, Map<String, Object> params) throws Exception {
        HttpPost httpPost = new HttpPost(url);
        config(httpPost);
        setPostParams(httpPost, params);
        HashMap<String, Object> headMap = new HashMap<>();
        headMap.put("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
        Set<String> keySet = headMap.keySet();
        for (String key : keySet) {
            httpPost.setHeader(key, headMap.get(key).toString());
        }
        CloseableHttpResponse response = null;
        try {
            response = getHttpClient(url).execute(httpPost,
                    HttpClientContext.create());
            HttpEntity entity = response.getEntity();
            String result = EntityUtils.toString(entity, "utf-8");
            EntityUtils.consume(entity);
            return result;
        } catch (Exception e) {
//          e.printStackTrace();
            throw new Exception("HttpClientUtils.postAndHeader() exception. -->" + e.getMessage());
        } finally {
            try {
                if (response != null)
                    response.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * 发送post请求并加请求头--json格式
     */
    public static String postStringEntityAndHeader(String url, String data, Map<String, Object> headMap) throws Exception {
        HttpPost httpPost = new HttpPost(url);
        httpPost.setHeader("Content-Type", "application/json");
        Set<String> keySet = headMap.keySet();
        for (String key : keySet) {
            httpPost.setHeader(key, headMap.get(key).toString());
        }
        config(httpPost);
        httpPost.setEntity(new StringEntity(data, "utf-8"));
        CloseableHttpResponse response = null;
        try {
            response = getHttpClient(url).execute(httpPost,
                    HttpClientContext.create());
            HttpEntity entity = response.getEntity();
            String result = EntityUtils.toString(entity, "utf-8");
            EntityUtils.consume(entity);
            return result;
        } catch (Exception e) {
          e.printStackTrace();
            throw new Exception("HttpClientUtils.postStringEntity() exception. -->" + e.getMessage());
        } finally {
            try {
                if (response != null){
                    response.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private static void setPostParams(HttpPost httpost,
                                      Map<String, Object> params) {
        List<NameValuePair> nvps = new ArrayList<NameValuePair>();
        Set<String> keySet = params.keySet();
        for (String key : keySet) {
            nvps.add(new BasicNameValuePair(key, params.get(key).toString()));
        }
        try {
            httpost.setEntity(new UrlEncodedFormEntity(nvps, "UTF-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

    /**
     * GET请求URL获取内容
     *
     * @param url
     * @return
     */
    public static String get(String url) throws Exception{
        HttpGet httpget = new HttpGet(url);
        config(httpget);
        CloseableHttpResponse response = null;
        try {
            response = getHttpClient(url).execute(httpget, HttpClientContext.create());
            HttpEntity entity = response.getEntity();
            String result = EntityUtils.toString(entity, "utf-8");
            EntityUtils.consume(entity);
            return result;
        } catch (IOException e) {
            log.error("GET请求URL获取内容失败!",e);
            throw new Exception("HttpClientUtils.get() exception. -->"+e.getMessage());
        } finally {
            try {
                if (response != null){
                    response.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}