使用 JAVA Swing 构建 Sftp 桌面连接工具

技术要点

  1. 将 logback 日志输出到 Swing 组件上。
  2. 使 jsch.jar 包连接 sftp 服务器、实现文件的上传下载。
  3. 最终是为了实现文件自动同步。

步骤一、将logback日志输出到Swing组件上

Stack Overflow上解决方案(因为自己也通过一些 csdn 的文章对于 AppenderBase 进行继承,但并未实现效果 ,后面直接去Stack Overflow上搜索就一次性解决了)

gitee源代码

温馨提示:

因为我们同步工具是需要部署在 windows 服务器上,所以才有将 logback 日志输出到 Swing 组件上,如果你们用的是 linux服务器,可以自行修改。

我在此基础上根据自己的需要进行了一些更改。下面是关键部分的代码

SwingClient

package com.blackdragon.sftp.swing;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
import javax.swing.plaf.nimbus.NimbusLookAndFeel;

import com.blackdragon.sftp.schedule.SftpSchedule;
import com.blackdragon.sftp.utils.SFTPUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;


@Component(value = "swingClient")
public class SwingClient {

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

	public static final SwingClient SWINGCLIENT;

	public SftpSchedule sftpSchedule;

	public SFTPUtil sftpUtil;

	static {
		// Look and Feel
		try {
			UIManager.setLookAndFeel(new NimbusLookAndFeel());
		} catch (UnsupportedLookAndFeelException e) {
			logger.error("Erro ao configurar NimbusLookAndFeel");
		}

		// Esse painel do form principal está sendo usando em outros lugares da aplicação
		SWINGCLIENT = new SwingClient();
	}

	public JFrame frame;
	public JPanel contentPane;
	public JPanel headPane;
	public JTextPane jTextPane;
	public JScrollPane logScrollPane;
	public JLabel lableApplicationStatus;

	/**
	 * Create the application.
	 */
	public SwingClient() {
		initialize();
	}

	public void initialize() {
		frame = new JFrame("v1.0 Balck_Dragon SFTP");
		// set window size
		frame.setBounds(0, 0, 1000, 800);
		// Set the default window closing method
		frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
		// Set the window to the center of the screen
		frame.setLocationRelativeTo(null);
		contentPane = new JPanel(new BorderLayout());
		contentPane.setBackground(Color.WHITE);
		jTextPane = new JTextPane();
		// Settings are not editable
		jTextPane.setEditable(false);

		// show with ScrollPane
		logScrollPane = new JScrollPane();
		logScrollPane.setBounds(30, 50, 900, 500);
		logScrollPane.setViewportView(jTextPane);

		frame.setContentPane(contentPane);

		final JButton btnStart = new JButton("启动-Start");
		final JButton btnStop = new JButton("停止-Stop");
		btnStart.setBounds(30, 15, 100, 30);
		btnStop.setBounds(150, 15, 100, 30);

		btnStart.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				btnStart.setEnabled(false);
				btnStop.setEnabled(true);
				startSwing();
			}
		});
		btnStop.setEnabled(false);
		btnStop.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				btnStart.setEnabled(true);
				btnStop.setEnabled(false);
				stopSwing();
			}
		});

		headPane = new JPanel(new FlowLayout(1,10,1));
		headPane.add("North", btnStart);
		headPane.add("North", btnStop);
		contentPane.add("North", headPane);

		contentPane.add("Center", logScrollPane);
		lableApplicationStatus = new JLabel("SftpSchedule Status : Stopped!", JLabel.CENTER);
		lableApplicationStatus.setFont(new Font("Calibri", Font.PLAIN, 15));
		lableApplicationStatus.setBounds(0, 100, 20, 15);
		contentPane.add("South", lableApplicationStatus);

	}

	private void startSwing() {
		sftpSchedule.start(sftpUtil);
		lableApplicationStatus.setText("SftpSchedule Status : Running!");
	}

	public void stopSwing() {
		sftpSchedule.stop();
		lableApplicationStatus.setText("SftpSchedule Status : Stopped!");
	}

	public JTextPane getTextPane() {
		return jTextPane;
	}
	
	public SFTPUtil getSftpUtil() {
		return sftpUtil;
	}

	public SftpSchedule getSftpSchedule() {
		return sftpSchedule;
	}

	public void setSftpSchedule(SftpSchedule sftpSchedule) {
		this.sftpSchedule = sftpSchedule;
	}

	public void setSftpUtil(SFTPUtil sftpUtil) {
		this.sftpUtil = sftpUtil;
	}

	public JFrame getFrame() {
		return frame;
	}

	public void setFrame(JFrame frame) {
		this.frame = frame;
	}

}

Appender

package com.blackdragon.sftp.logger;


import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.PatternLayout;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.AppenderBase;
import com.blackdragon.sftp.swing.SwingClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.swing.*;
import javax.swing.text.*;
import java.awt.*;

/**
 * @author Rodrigo Garcia Lima (email: rodgarcialima@gmail.com | github: rodgarcialima)
 * @see ch.qos.logback.core.AppenderBase
 */
public class Appender extends AppenderBase<ILoggingEvent> {
    private final static Logger log = LoggerFactory.getLogger(Appender.class);

    /**
     * Utilizo para formatar a mensagem de log
     */
    private PatternLayout patternLayout;

    /**
     * Cada nível de log tem um estilo próprio
     */
    private static SimpleAttributeSet ERROR_ATT, WARN_ATT, INFO_ATT, DEBUG_ATT, TRACE_ATT, RESTO_ATT;

    /**
     * Definição dos estilos de log
     */
    static {
        // ERROR
        ERROR_ATT = new SimpleAttributeSet();
        ERROR_ATT.addAttribute(StyleConstants.CharacterConstants.Bold, Boolean.TRUE);
        ERROR_ATT.addAttribute(StyleConstants.CharacterConstants.Italic, Boolean.FALSE);
        ERROR_ATT.addAttribute(StyleConstants.CharacterConstants.Foreground, new Color(153, 0, 0));

        // WARN
        WARN_ATT = new SimpleAttributeSet();
        WARN_ATT.addAttribute(StyleConstants.CharacterConstants.Bold, Boolean.FALSE);
        WARN_ATT.addAttribute(StyleConstants.CharacterConstants.Italic, Boolean.FALSE);
        WARN_ATT.addAttribute(StyleConstants.CharacterConstants.Foreground, new Color(153, 76, 0));

        // INFO
        INFO_ATT = new SimpleAttributeSet();
        INFO_ATT.addAttribute(StyleConstants.CharacterConstants.Bold, Boolean.FALSE);
        INFO_ATT.addAttribute(StyleConstants.CharacterConstants.Italic, Boolean.FALSE);
        INFO_ATT.addAttribute(StyleConstants.CharacterConstants.Foreground, new Color(0, 0, 153));

        // DEBUG
        DEBUG_ATT = new SimpleAttributeSet();
        DEBUG_ATT.addAttribute(StyleConstants.CharacterConstants.Bold, Boolean.FALSE);
        DEBUG_ATT.addAttribute(StyleConstants.CharacterConstants.Italic, Boolean.TRUE);
        DEBUG_ATT.addAttribute(StyleConstants.CharacterConstants.Foreground, new Color(64, 64, 64));

        // TRACE
        TRACE_ATT = new SimpleAttributeSet();
        TRACE_ATT.addAttribute(StyleConstants.CharacterConstants.Bold, Boolean.FALSE);
        TRACE_ATT.addAttribute(StyleConstants.CharacterConstants.Italic, Boolean.TRUE);
        TRACE_ATT.addAttribute(StyleConstants.CharacterConstants.Foreground, new Color(153, 0, 76));

        // RESTO
        RESTO_ATT = new SimpleAttributeSet();
        RESTO_ATT.addAttribute(StyleConstants.CharacterConstants.Bold, Boolean.FALSE);
        RESTO_ATT.addAttribute(StyleConstants.CharacterConstants.Italic, Boolean.TRUE);
        RESTO_ATT.addAttribute(StyleConstants.CharacterConstants.Foreground, new Color(0, 0, 0));
    }

    @Override
    public void start() {
        patternLayout = new PatternLayout();
        patternLayout.setContext(getContext());
        patternLayout.setPattern("%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n");
        patternLayout.start();

        super.start();
    }

    @Override
    protected void append(ILoggingEvent event) {
        /* Formata mensagem do log */
        String formattedMsg = patternLayout.doLayout(event);

        // Forma segura de atualizar o JTextpane
        SwingUtilities.invokeLater(() -> {
            // Alias for JTextPhone JScrollPane in the application
            JTextPane textPane = SwingClient.SWINGCLIENT.jTextPane;
            JScrollPane logScrollPane = SwingClient.SWINGCLIENT.logScrollPane;

            try {
                // Trunca linhas para economizar memória
                // Quando atingir 2000 linhas, eu quero que
                // apague as 500 primeiras linhas
                int limite = 1000;
                int apaga = 200;
                if (textPane.getDocument().getDefaultRootElement().getElementCount() > limite) {
                    int end = getLineEndOffset(textPane, apaga);
                    replaceRange(textPane, null, 0, end);
                }

                // Decide qual atributo (estilo) devo usar de acordo com o nível o log
                if (event.getLevel() == Level.ERROR) {
                    textPane.getDocument().insertString(textPane.getDocument().getLength(), formattedMsg, ERROR_ATT);
                } else if (event.getLevel() == Level.WARN) {
                    textPane.getDocument().insertString(textPane.getDocument().getLength(), formattedMsg, WARN_ATT);
                } else if (event.getLevel() == Level.INFO) {
                    textPane.getDocument().insertString(textPane.getDocument().getLength(), formattedMsg, INFO_ATT);
                } else if (event.getLevel() == Level.DEBUG) {
                    textPane.getDocument().insertString(textPane.getDocument().getLength(), formattedMsg, DEBUG_ATT);
                } else if (event.getLevel() == Level.TRACE) {
                    textPane.getDocument().insertString(textPane.getDocument().getLength(), formattedMsg, TRACE_ATT);
                } else {
                    textPane.getDocument().insertString(textPane.getDocument().getLength(), formattedMsg, RESTO_ATT);
                }
                // Set scrollbar position
                logScrollPane.getVerticalScrollBar().setValue(logScrollPane.getVerticalScrollBar().getMaximum());
            } catch (BadLocationException e) {
                log.error("error: {}", e.getMessage());
            }

            // Vai para a última linha
            textPane.setCaretPosition(textPane.getDocument().getLength());
        });
    }

    /**
     * Código copiado do {@link JTextArea#getLineCount()}
     * @param textPane de onde quero as linhas contadas
     * @return quantidade de linhas &gt; 0
     */
    private int getLineCount(JTextPane textPane) {
        return textPane.getDocument().getDefaultRootElement().getElementCount();
    }

    /**
     * Código copiado do {@link JTextArea#getLineEndOffset(int)}
     * @param textPane de onde quero o offset
     * @param line the line &gt;= 0
     * @return the offset &gt;= 0
     * @throws BadLocationException Thrown if the line is
     * less than zero or greater or equal to the number of
     * lines contained in the document (as reported by
     * getLineCount)
     */
    private int getLineEndOffset(JTextPane textPane, int line) throws BadLocationException {
        int lineCount = getLineCount(textPane);
        if (line < 0) {
            throw new BadLocationException("Negative line", -1);
        } else if (line >= lineCount) {
            throw new BadLocationException("No such line", textPane.getDocument().getLength()+1);
        } else {
            Element map = textPane.getDocument().getDefaultRootElement();
            Element lineElem = map.getElement(line);
            int endOffset = lineElem.getEndOffset();
            // hide the implicit break at the end of the document
            return ((line == lineCount - 1) ? (endOffset - 1) : endOffset);
        }
    }

    /**
     * Código copiado do {@link JTextArea#replaceRange(String, int, int)}<br>
     *
     * Replaces text from the indicated start to end position with the
     * new text specified.  Does nothing if the model is null.  Simply
     * does a delete if the new string is null or empty.<br>
     *
     * @param textPane de onde quero substituir o texto
     * @param str the text to use as the replacement
     * @param start the start position &gt;= 0
     * @param end the end position &gt;= start
     * @exception IllegalArgumentException if part of the range is an invalid position in the model
     */
    private void replaceRange(JTextPane textPane, String str, int start, int end) throws IllegalArgumentException {
        if (end < start) {
            throw new IllegalArgumentException("end before start");
        }
        Document doc = textPane.getDocument();
        if (doc != null) {
            try {
                if (doc instanceof AbstractDocument) {
                    ((AbstractDocument)doc).replace(start, end - start, str, null);
                }
                else {
                    doc.remove(start, end - start);
                    doc.insertString(start, str, null);
                }
            } catch (BadLocationException e) {
                throw new IllegalArgumentException(e.getMessage());
            }
        }
    }
}

logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true" scan="true" scanPeriod="1 seconds">

    <contextName>logback</contextName>
    <!--定义参数,后面可以通过${app.name}使用-->
    <property name="app.name" value="logback_test"/>
    <!--ConsoleAppender 用于在屏幕上输出日志-->
    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
        <!--定义了一个过滤器,在LEVEL之下的日志输出不会被打印出来-->
        <!--这里定义了DEBUG,也就是控制台不会输出比ERROR级别小的日志-->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>INFO</level>
        </filter>
        <!-- encoder 默认配置为PatternLayoutEncoder -->
        <!--定义控制台输出格式-->
        <encoder>
            <pattern>%d [%thread] %-5level %logger{36} [%file : %line] - %msg%n</pattern>
        </encoder>
    </appender>
    
    <appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!--定义日志输出的路径-->
        <!--这里的scheduler.manager.server.home 没有在上面的配置中设定,所以会使用java启动时配置的值-->
        <!--比如通过 java -Dscheduler.manager.server.home=/path/to XXXX 配置该属性-->
        <file>${scheduler.manager.server.home}/logs/${app.name}.log</file>
        <!--定义日志滚动的策略-->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--定义文件滚动时的文件名的格式-->
            <fileNamePattern>${scheduler.manager.server.home}/logs/${app.name}.%d{yyyy-MM-dd.HH}.log.gz
            </fileNamePattern>
            <!--60天的时间周期,日志量最大20GB-->
            <maxHistory>60</maxHistory>
            <!-- 该属性在 1.1.6版本后 才开始支持-->
            <totalSizeCap>20GB</totalSizeCap>
        </rollingPolicy>
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <!--每个日志文件最大100MB-->
            <maxFileSize>100MB</maxFileSize>
        </triggeringPolicy>
        <!--定义输出格式-->
        <encoder>
            <pattern>%d [%thread] %-5level %logger{36} [%file : %line] - %msg%n</pattern>
        </encoder>
    </appender>

    <appender name="sftpAppender" class="com.blackdragon.sftp.logger.Appender" />
    <!--root是默认的logger 这里设定输出级别是debug-->
    <root level="trace">
        <!--定义了两个appender,日志会通过往这两个appender里面写-->
        <appender-ref ref="stdout"/>
        <appender-ref ref="sftpAppender"/>
    </root>

</configuration>

步骤二:使用 jsch 连接 sftp 服务器

SftpConfig

package com.blackdragon.sftp.config;

import com.blackdragon.sftp.common.constant.Constants;
import com.blackdragon.sftp.utils.SFTPUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

import java.util.Objects;

/**
 * @author: Black_Dragon
 * @date: 2022/8/31
 */
@Configuration
public class SftpConfig {

    @Value("${sftp.host}")
    private String host;
    @Value("${sftp.port}")
    private int port;
    @Value("${sftp.username}")
    private String username;
    @Value("${sftp.password}")
    private String password;
    @Value("${sftp.privateKey}")
    private String privateKey;
    @Value("${sftp.authMethod}")
    private Integer authMethod;

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

    public String getPrivateKey() {
        return privateKey;
    }

    public void setPrivateKey(String privateKey) {
        this.privateKey = privateKey;
    }

    public SFTPUtil getSftpUtil(){
        if(Objects.equals(authMethod, Constants.KEY_VERIFICATION)){
            return new SFTPUtil(username, host, port, privateKey);
        }
        return new SFTPUtil(username, password, host, port);
    }
}

SFTPUtil

package com.blackdragon.sftp.utils;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.*;

import com.blackdragon.sftp.domain.SftpFile;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpException;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author: Black_Dragon
 * @date: 2022/8/30
 */
public class SFTPUtil {

	private final static Logger log = LoggerFactory.getLogger(SFTPUtil.class);
	private ChannelSftp sftp;

	private Session session;
	/** FTP 登录用户名*/
	private String username;
	/** FTP 登录密码*/
	private String password;
	/** 私钥 */
	private String privateKey;
	/** FTP 服务器地址IP地址*/
	private String host;
	/** FTP 端口*/
	private int port;


	/**
	 * 构造基于密码认证的sftp对象
	 * @param username
	 * @param password
	 * @param host
	 * @param port
	 */
	public SFTPUtil(String username, String password, String host, int port) {
		this.username = username;
		this.password = password;
		this.host = host;
		this.port = port;
		login();
	}

	/**
	 * 构造基于秘钥认证的sftp对象
	 * @param username
	 * @param host
	 * @param port
	 * @param privateKey
	 */
	public SFTPUtil(String username, String host, int port, String privateKey) {
		this.username = username;
		this.host = host;
		this.port = port;
		this.privateKey = privateKey;
		login();
	}

	/**
	 * 连接sftp服务器
	 * @throws JSchException
	 *
	 * @throws Exception
	 */
	private void login(){
		try {
			JSch jsch = new JSch();
			if (privateKey != null) {
				jsch.addIdentity(privateKey);// 设置私钥
				log.info("sftp connect,path of private key file:{}" , privateKey);
			}
			log.info("sftp connect by host:{} username:{}",host,username);

			session = jsch.getSession(username, host, port);
			log.info("Session is build");
			if (password != null) {
				session.setPassword(password);
			}
			Properties config = new Properties();
			config.put("StrictHostKeyChecking", "no");

			session.setConfig(config);
			session.connect();
			log.info("Session is connected");

			Channel channel = session.openChannel("sftp");
			channel.connect();
			log.info("channel is connected");

			sftp = (ChannelSftp) channel;
			log.info(String.format("sftp server host:[%s] port:[%s] is connect successfull", host, port));
		} catch (JSchException e) {
			log.error("Cannot connect to specified sftp server : {}:{} \n Exception message is: {}", new Object[]{host, port, e.getMessage()});
//	        	throw e;

		}
	}

	/**
	 * 关闭连接 server
	 */
	public void logout(){
		if (sftp != null) {
			if (sftp.isConnected()) {
				sftp.disconnect();
				log.info("sftp is closed already");
			}
		}
		if (session != null) {
			if (session.isConnected()) {
				session.disconnect();
				log.info("sshSession is closed already");
			}
		}
	}

	/**
	 * 将输入流的数据上传到sftp作为文件
	 *
	 * @param directory
	 *            上传到该目录
	 * @param sftpFileName
	 *            sftp端文件名
	 * @param input
	 *            输入流
	 * @throws SftpException
	 * @throws Exception
	 */
	public void upload(String directory, String sftpFileName, InputStream input) throws SftpException{
		log.info("file:{} begin upload" , sftpFileName);
		try {
			sftp.cd(directory);
		} catch (SftpException e) {
			log.warn("{}, directory is not exist,{}", directory, e.getMessage());
			sftp.mkdir(directory);
			sftp.cd(directory);
		}
		sftp.put(input, sftpFileName);
		sftp.cd("..");
		try {
			input.close(); //必须关闭资源,不然无法删除文件
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		log.info("file:{} is upload successful" , sftpFileName);
	}

	/**
	 * 上传单个文件
	 *
	 * @param directory
	 *            上传到sftp目录
	 * @param uploadFile
	 *            要上传的文件,包括路径
	 * @throws FileNotFoundException
	 * @throws SftpException
	 * @throws Exception
	 */
	public void upload(String directory, String uploadFile) throws FileNotFoundException, SftpException{
		File file = new File(uploadFile);
		upload(directory, file.getName(), new FileInputStream(file));
	}

	/**
	 * 将byte[]上传到sftp,作为文件。注意:从String生成byte[]是,要指定字符集。
	 *
	 * @param directory
	 *            上传到sftp目录
	 * @param sftpFileName
	 *            文件在sftp端的命名
	 * @param byteArr
	 *            要上传的字节数组
	 * @throws SftpException
	 * @throws Exception
	 */
	public void upload(String directory, String sftpFileName, byte[] byteArr) throws SftpException{
		upload(directory, sftpFileName, new ByteArrayInputStream(byteArr));
	}

	/**
	 * 将字符串按照指定的字符编码上传到sftp
	 *
	 * @param directory
	 *            上传到sftp目录
	 * @param sftpFileName
	 *            文件在sftp端的命名
	 * @param dataStr
	 *            待上传的数据
	 * @param charsetName
	 *            sftp上的文件,按该字符编码保存
	 * @throws UnsupportedEncodingException
	 * @throws SftpException
	 * @throws Exception
	 */
	public void upload(String directory, String sftpFileName, String dataStr, String charsetName) throws UnsupportedEncodingException, SftpException{
		upload(directory, sftpFileName, new ByteArrayInputStream(dataStr.getBytes(charsetName)));
	}

	/**
	 * 下载文件
	 *
	 * @param directory
	 *            下载目录
	 * @param downloadFile
	 *            下载的文件
	 * @param saveFile
	 *            存在本地的路径
	 * @throws SftpException
	 * @throws FileNotFoundException
	 * @throws Exception
	 */
	public void download(String directory, String downloadFile, String saveFile) throws SftpException, FileNotFoundException{
		if (directory != null && !"".equals(directory)) {
			sftp.cd(directory);
		}
		File file = new File(saveFile);
		sftp.get(downloadFile, new FileOutputStream(file));
		log.info("file:{} is download successful" , downloadFile);
	}
	/**
	 * 下载文件
	 * @param directory 下载目录
	 * @param downloadFile 下载的文件名
	 * @return 字节数组
	 * @throws SftpException
	 * @throws IOException
	 * @throws Exception
	 */
	public byte[] download(String directory, String downloadFile) throws SftpException, IOException{
		if (directory != null && !"".equals(directory)) {
			sftp.cd(directory);
		}
		InputStream is = sftp.get(downloadFile);

		byte[] fileData = IOUtils.toByteArray(is);

		log.info("file:{} is download successful" , downloadFile);
		return fileData;
	}

	/**
	 * 删除文件
	 *
	 * @param directory
	 *            要删除文件所在目录
	 * @param deleteFile
	 *            要删除的文件
	 * @throws SftpException
	 * @throws Exception
	 */
	public void delete(String directory, String deleteFile) throws SftpException{
		sftp.cd(directory);
		sftp.rm(deleteFile);
	}

	/**
	 * 列出目录下的文件
	 *
	 * @param directory
	 * @return List<SftpFile>
	 * @throws SftpException
	 */
	public List<SftpFile> listFiles(String directory) throws SftpException {
		List<SftpFile> sftpFileList = new ArrayList<>();
		sftp.ls(directory).forEach(vector -> {
			SftpFile sftpFile = new SftpFile();
			ChannelSftp.LsEntry lsEntry = (ChannelSftp.LsEntry) vector;
			sftpFile.setFilename(lsEntry.getFilename());
			sftpFile.setLongname(lsEntry.getLongname());
			sftpFile.setSize(lsEntry.getAttrs().getSize());
			sftpFile.setAtime(lsEntry.getAttrs().getATime());
			sftpFile.setMtime(lsEntry.getAttrs().getMTime());
			sftpFile.setFlags(lsEntry.getAttrs().getFlags());
			sftpFile.setGid(lsEntry.getAttrs().getGId());

			sftpFileList.add(sftpFile);
		});

		return sftpFileList;
	}
}

效果图
在这里插入图片描述在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值