手把手教你java实现sftp上传文件到linux服务器

1、首先引入pom文件依赖

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

2、创建sftp工厂

package com.ztc.lrh.web.config;

import com.jcraft.jsch.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;

@Slf4j
public class ChannelSftpFactory extends BasePooledObjectFactory<ChannelSftp> {
	
	public ChannelSftpFactory(String host, Integer port, String protocol, String username, String password,
			String sessionStrictHostKeyChecking, Integer sessionConnectTimeout, Integer channelConnectedTimeout) {
		super();
		this.host = host;
		this.port = port;
		this.protocol = protocol;
		this.username = username;
		this.password = password;
		this.sessionStrictHostKeyChecking = sessionStrictHostKeyChecking;
		this.sessionConnectTimeout = sessionConnectTimeout;
		this.channelConnectedTimeout = channelConnectedTimeout;
	}

	private String host;// host

	private Integer port;// 端口

	private String protocol;// 协议

	private String username;// 用户

	private String password;// 密码

	private String sessionStrictHostKeyChecking;

	private Integer sessionConnectTimeout;// session超时时间

	private Integer channelConnectedTimeout;// channel超时时间

	// 设置第一次登陆的时候提示,可选值:(ask | yes | no)
	private static final String SESSION_CONFIG_STRICT_HOST_KEY_CHECKING = "StrictHostKeyChecking";

	@Override
	public ChannelSftp create() throws Exception {
		return null;
	}

	@Override
	public PooledObject<ChannelSftp> makeObject() throws Exception {
		JSch jsch = new JSch();
		//log.info("sftp尝试连接[" + username + "@" + host + "], use password[" + password + "]");
		Session session = createSession(jsch, host, username, port);
		session.setPassword(password);
		session.connect(sessionConnectTimeout);
		//log.info("sftp已连接到:{}", host);
		Channel channel = session.openChannel(protocol);
		channel.connect(channelConnectedTimeout);
		//log.info("sftp创建channel:{}", host);
		return new DefaultPooledObject<ChannelSftp>((ChannelSftp) channel);
	}

	@Override
	public PooledObject<ChannelSftp> wrap(ChannelSftp channelSftp) {
		return new DefaultPooledObject<>(channelSftp);
	}

	@Override
	public void destroyObject(PooledObject<ChannelSftp> sftpPooled) throws Exception {
		ChannelSftp sftp = sftpPooled.getObject();
		try {
			if (sftp != null) {
				if (sftp.isConnected()) {
					sftp.disconnect();
				} else if (sftp.isClosed()) {
					log.info("sftp连接已关闭");
				}
				if (null != sftp.getSession()) {
					sftp.getSession().disconnect();
				}
			}
		} catch (JSchException e) {
			log.error("关闭sftp连接失败: {}", e);
			e.printStackTrace();
			throw new Exception("关闭sftp连接失败");
		}
	}

	@Override
	public boolean validateObject(PooledObject<ChannelSftp> sftpPooled) {
		try {
			ChannelSftp sftp = sftpPooled.getObject();
			if (sftp != null) {
				return (sftp.isConnected() && sftp.getSession().isConnected());
			}
		} catch (JSchException e) {
			log.error("sftp连接校验失败: {}", e);
			e.printStackTrace();
			throw new RuntimeException("sftp连接校验失败");
		}
		return false;
	}

	/**
	 * 创建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 RuntimeException(host + " sftp建立连接session is null");
		}
		session.setConfig(SESSION_CONFIG_STRICT_HOST_KEY_CHECKING, sessionStrictHostKeyChecking);
		return session;
	}
}

3、添加配置

sftp:
  client:
    protocol: ${sftpProtocolValue}
    host: ${sftpHost}
    port: ${sftpPort}
    username: ${sftpUser}
    password: ${sftpPwd}
    root: ${sftpRoot}
    privateKey: ${sftpPrivateKey}
    passphrase: ${sftpPass}
    sessionStrictHostKeyChecking: ${sftpSession}
    sessionConnectTimeout: ${sftpSessionTime}
    channelConnectedTimeout: ${sftpChannel}
    serverUrl: ${sftpUrl}
    targetPath: ${sftpTarget}
    reservedName: ${sftpReserved}

实际值根据自己情况补充

4、创建sftp配置类

package com.ztc.lrh.web.config;

import com.jcraft.jsch.ChannelSftp;
import lombok.Data;
import org.apache.commons.pool2.impl.AbandonedConfig;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

@Configuration
@Component
@Data
public class SftpConfiguration {
	
	public GenericObjectPool<ChannelSftp> channelSftpPool;//连接池
	
	@Value("${sftp.client.host}")
	private String host;//host

	@Value("${sftp.client.port}")
    private Integer port;//端口

	@Value("${sftp.client.protocol}")
    private String protocol;//协议

	@Value("${sftp.client.username}")
    private String username;//用户

	@Value("${sftp.client.password}")
    private String password;//密码

	@Value("${sftp.client.root}")
    private String root;//根路径

	@Value("${sftp.client.privateKey}")
    private String privateKey;//密匙路径

	@Value("${sftp.client.passphrase}")
    private String passphrase;//密匙密码

	@Value("${sftp.client.sessionStrictHostKeyChecking}")
    private String sessionStrictHostKeyChecking;

	@Value("${sftp.client.sessionConnectTimeout}")
    private Integer sessionConnectTimeout;//session超时时间

	@Value("${sftp.client.channelConnectedTimeout}")
    private Integer channelConnectedTimeout;//channel超时时间
	
	@Value("${sftp.client.serverUrl}")
	private String serverUrl;//图片服务url
	
	/**   
	 * @Title: getSftpSocket   
	 * @Description: 获取连接  
	 * @param: @return      
	 * @return: ChannelSftp      
	 * @throws   
	 */ 
	public ChannelSftp getSftpSocket(){
		try {
			if(null == this.channelSftpPool){
				initPool();
			}
			return this.channelSftpPool.borrowObject();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/**   
	 * @Title: returnSftpSocket   
	 * @Description: 归还连接  
	 * @param: @param sftp      
	 * @return: void      
	 * @throws   
	 */ 
	public void returnSftpSocket(ChannelSftp sftp){
		this.channelSftpPool.returnObject(sftp);
	}
	
	/**   
	 * @Title: initPool   
	 * @Description: 初始化连接池  
	 * @param:       
	 * @return: void      
	 * @throws   
	 */ 
	private void initPool(){
		GenericObjectPoolConfig config=new GenericObjectPoolConfig();
		config.setMinIdle(10);
		config.setMaxTotal(10);
		config.setMaxWaitMillis(30000);
		this.channelSftpPool = new GenericObjectPool<>(new ChannelSftpFactory(host, port, protocol, username, password,
				sessionStrictHostKeyChecking, sessionConnectTimeout, channelConnectedTimeout),config);
		AbandonedConfig abandonedConfig = new AbandonedConfig();
		abandonedConfig.setRemoveAbandonedOnMaintenance(true); //在Maintenance的时候检查是否有泄漏
		abandonedConfig.setRemoveAbandonedOnBorrow(true); //borrow 的时候检查泄漏
		abandonedConfig.setRemoveAbandonedTimeout(10); //如果一个对象borrow之后10秒还没有返还给pool,认为是泄漏的对象
		this.channelSftpPool.setAbandonedConfig(abandonedConfig);
		this.channelSftpPool.setTimeBetweenEvictionRunsMillis(30000); //30秒运行一次维护任务
	}
}

5、创建文件上传controller

package com.ztc.lrh.web.controller;

import com.ztc.common.api.result.annotation.ResultResponse;
import com.ztc.common.api.result.data.CollectionData;
import com.ztc.common.api.result.result.ResultEnum;
import com.ztc.lrh.web.common.Constants;
import com.ztc.lrh.web.service.impl.FileSystemService;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.util.*;
import java.util.stream.Collectors;

@RestController
@Slf4j
@Api(tags = "sftp")
@CrossOrigin("*")
@ResultResponse
public class SftpController {

    @Autowired
    FileSystemService service;

    @Value("${sftp.client.targetPath}")
    private String targetPath;

    @Value("${sftp.client.reservedName}")
    private boolean reservedName;


    @PostMapping("/sftp")
    public CollectionData<String> upload(@RequestParam(required = true) MultipartFile[] files){
        CollectionData<String> collectionData =new CollectionData<String>();
        if(files==null || files.length == 0){
            Map<String,Object> map = new HashMap<>();
            map.put("error","上传失败,请选择上传文件");
            collectionData.setMeta(map);
            return collectionData;
        }
        List<String> imageUrls;
        try {
            imageUrls = service.uploadFile(targetPath, files, reservedName);
            collectionData.setList(imageUrls);

            return collectionData;
        } catch (Exception e) {
            e.printStackTrace();
        }
        Map<String,Object> map = new HashMap<>();
        map.put("error","上传文件失败");
        collectionData.setMeta(map);
        return collectionData;
    }

    @DeleteMapping("/sftp")
    public ResultEnum delete(String[] fileNames){
        try {
            if(fileNames==null || fileNames.length == 0){
                return ResultEnum.STRING.setData("请选择删除文件");
            }
            List<String> urlList = new ArrayList<String>();
            urlList = Arrays.asList(fileNames).stream().map(e ->{ return targetPath+ Constants.SPLIT_DIR+e;}).collect(Collectors.toList());
            if(service.deleteFile(urlList)){
                return ResultEnum.STRING.setData("success");
            }else {
                return ResultEnum.STRING.setData("error");
            }

        } catch (Exception e) {
            e.printStackTrace();
            return ResultEnum.STRING.setData("删除文件失败");
        }

    }
}

6、创建文件上传service

package com.ztc.lrh.web.service.impl;

import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.SftpException;
import com.ztc.lrh.web.config.SftpConfiguration;
import com.ztc.lrh.web.utils.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@Service
@Slf4j
public class FileSystemService {


    @Autowired
    private SftpConfiguration config;


    /**
     * @Title: createDirs @Description: 创建层级目录 @param: @param
     * dirPath @param: @param sftp @param: @return @return: boolean @throws
     */
    private boolean createDirs(String dirPath, ChannelSftp sftp) {
        if (dirPath != null && !dirPath.isEmpty() && sftp != null) {
            String[] dirs = Arrays.stream(dirPath.split("/")).filter(StringUtils::hasLength).toArray(String[]::new);

            for (String dir : dirs) {
                try {
                    sftp.cd(dir);
                    //log.info("stfp切换到目录:{}", dir);
                } catch (Exception e) {
                    try {
                        sftp.mkdir(dir);
                        //log.info("sftp创建目录:{}", dir);
                    } catch (SftpException e1) {
                        log.error("[sftp创建目录失败]:{}", dir, e1);
                        e1.printStackTrace();
                        throw new RuntimeException("sftp创建目录失败");
                    }
                    try {
                        sftp.cd(dir);
                        //log.info("stfp切换到目录:{}", dir);
                    } catch (SftpException e1) {
                        log.error("[sftp切换目录失败]:{}", dir, e1);
                        e1.printStackTrace();
                        throw new RuntimeException("sftp切换目录失败");
                    }
                }
            }
            return true;
        }
        return false;
    }
    public List<String> uploadFile(String targetPath, MultipartFile[] files, boolean reservedName) throws Exception {
        ChannelSftp sftp = config.getSftpSocket();
        List<String> resultStrs = new ArrayList<String>();
        try {
            sftp.cd(config.getRoot());
            //log.info("sftp连接host", config.getRoot());
            boolean dirs = this.createDirs(targetPath, sftp);
            if (!dirs) {
                log.error("sftp创建目录失败", targetPath);
                throw new RuntimeException("上传文件失败");
            }
            for (MultipartFile file : files) {
                String fileName = "";
                if (reservedName) {
                    fileName = file.getOriginalFilename().substring(0, file.getOriginalFilename().lastIndexOf("."))
                            /*+ "-" + DateUtil.getCurrentDateTimeSecond()*/
                            +file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
                } else {
                    fileName = DateUtil.getCurrentDateTimeMilliSecond()
                            + file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
                }
                sftp.put(file.getInputStream(), fileName);
                resultStrs.add(config.getServerUrl() + targetPath + "/" + fileName);
            }
            return resultStrs;
        } catch (Exception e) {
            log.error("上传文件失败", targetPath, e);
            e.printStackTrace();
            throw new RuntimeException("上传文件失败");
        } finally {
            config.returnSftpSocket(sftp);
        }
    }

    public boolean deleteFile(List<String> targetPath) throws Exception {
        ChannelSftp sftp = null;
        try {
            sftp = config.getSftpSocket();
            sftp.cd(config.getRoot());
            for (String path : targetPath) {
                sftp.rm(path);
            }
            return true;
        } catch (Exception e) {
            log.error("删除文件失败", targetPath, e);
            e.printStackTrace();
            throw new RuntimeException("删除文件失败");
        } finally {
            config.returnSftpSocket(sftp);
        }
    }
}

前台直接访问controller接口,就可以实现上传、删除文件 ,价值1万的教程免费倾授,wish u all better,world peace。

OVER!!


版权声明:本文为lovestmost原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。