【java】java sftp访问文件夹,java smb访问共享文件夹 按时间过滤查找文件 集成springboot

tips: 集成springboot与不集成springboot区别不大,springboot中无非是引入一个maven依赖 加一个@Component注解 , 默认是单例;
复制代码前 请先认真看注意事项

java的sftp传输

依赖:

      <!-- sftp -->
        <dependency>
            <groupId>com.jcraft</groupId>
            <artifactId>jsch</artifactId>
            <version>0.1.49</version>
        </dependency>

核心代码:


import com.jcraft.jsch.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.util.Properties;
import java.util.Vector;


@Slf4j
@Component
public class SftpUtils {
    
    


    private Session sshSession;

    private ChannelSftp sftp;

    /**
     * 连接sftp服务器
     *
     * @param host
     * @param port
     * @param username
     * @param password
     * @return
     * @throws Exception
     */
    public ChannelSftp connect(String host, int port, String username, String password) throws Exception {
    
    
        JSch jsch = new JSch();
        sshSession = jsch.getSession(username, host, port);

        log.debug("Session created.");

        sshSession.setPassword(password);
        Properties sshConfig = new Properties();
        sshConfig.put("StrictHostKeyChecking", "no");
        sshSession.setConfig(sshConfig);
        sshSession.connect();

        log.debug("Session connected.");
        log.debug("Opening Channel.");

        Channel channel = sshSession.openChannel("sftp");
        channel.connect();
        sftp = (ChannelSftp) channel;

        log.debug("Connected to " + host + ".");

        return sftp;
    }

    /**
     * 连接sftp服务器
     *
     * @param host
     * @param port
     * @param username
     * @param privateKey
     * @param passphrase
     * @return
     * @throws Exception
     */
    public ChannelSftp connect(String host, int port, String username, String privateKey, String passphrase) throws Exception {
    
    
        JSch jsch = new JSch();

        //设置密钥和密码
        if (!StringUtils.isEmpty(privateKey)) {
    
    
            if (!StringUtils.isEmpty(passphrase)) {
    
    
                //设置带口令的密钥
                jsch.addIdentity(privateKey, passphrase);
            } else {
    
    
                //设置不带口令的密钥
                jsch.addIdentity(privateKey);
            }
        }
        sshSession = jsch.getSession(username, host, port);

        log.debug("Session created.");

        Properties sshConfig = new Properties();
        sshConfig.put("StrictHostKeyChecking", "no");
        sshSession.setConfig(sshConfig);
        sshSession.connect();

        log.debug("Session connected.");
        log.debug("Opening Channel.");

        Channel channel = sshSession.openChannel("sftp");
        channel.connect();
        sftp = (ChannelSftp) channel;

        log.debug("Connected to " + host + ".");

        return sftp;
    }

    public void portForwardingL(int lport, String rhost, int rport) throws Exception {
    
    
        int assinged_port = sshSession.setPortForwardingL(lport, rhost, rport);
        System.out.println("localhost:" + assinged_port + " -> " + rhost + ":" + rport);
    }

    /**
     * 断开连接
     */
    public void disconnect() {
    
    
        if (sftp != null) sftp.disconnect();
        if (sshSession != null) sshSession.disconnect();
    }

    /**
     * 上传文件
     *
     * @param directory  服务器的目录路径
     * @param uploadFile 要上传的文件路径
     *                   <p>
     *                   File path = new File(ResourceUtils.getURL("classpath:").getPath());
     *                   path.getAbsolutePath() : 相对路径 即class目录下
     */
    public void upload(String directory, String uploadFile) throws Exception {
    
    
        sftp.cd(directory);
        File file = new File(uploadFile);
        sftp.put(new FileInputStream(file), file.getName());
    }

    /**
     * 上传文件
     *
     * @param directory 服务器的目录路径
     * @param file      要上传的文件 例如 new File("Dockerfile") 是springboot项目根目录下的Dockerfile文件
     * @throws Exception
     */
    public void upload(String directory, File file) throws Exception {
    
    
        sftp.cd(directory);
        sftp.put(new FileInputStream(file), file.getName());
        log.info("upload file " + file.getAbsolutePath() + " to host " + sshSession.getHost());
    }

    //利用流上传文件 fileName
    public void uploadFileInputStream(MultipartFile file, String directory, String fileName) throws Exception {
    
    
        sftp.cd(directory);
        sftp.put(file.getInputStream(), fileName);
    }

    public void uploadDir(File src, String dst) throws Exception {
    
    
        if (!exist(dst)) {
    
    
            sftp.mkdir(dst);
        }
        if (src.isFile()) {
    
    
            upload(dst, src);
        } else {
    
    
            for (File file : src.listFiles()) {
    
    
                if (file.isDirectory()) {
    
    
                    uploadDir(file, dst + "/" + file.getName());
                }
                upload(dst, file);
            }
        }
    }

    /**
     * 目录是否查找
     *
     * @param path
     * @return
     * @throws SftpException
     */
    public boolean exist(String path) throws SftpException {
    
    
        String pwd = sftp.pwd();
        try {
    
    
            sftp.cd(path);
        } catch (SftpException e) {
    
    
            if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
    
    
                return false;
            } else {
    
    
                throw e;
            }
        } finally {
    
    
            sftp.cd(pwd);
        }

        return true;
    }

    /**
     * 下载文件
     *
     * @param directory    服务器目录
     * @param downloadFile 要下载的文件名
     * @param saveFile     输出文件名
     * @throws Exception
     */
    public void download(String directory, String downloadFile, String saveFile) throws Exception {
    
    
        try {
    
    
            sftp.cd(directory);
            File file = new File(saveFile);
            sftp.get(downloadFile, new FileOutputStream(file));
        } catch (SftpException e) {
    
    
            e.printStackTrace();
        } catch (FileNotFoundException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            disconnect();
        }
    }

    /**
     * 下载文件
     *
     * @param directory
     * @param downloadFile
     * @param saveFile
     * @throws Exception
     */
    public void download(String directory, String downloadFile, File saveFile) throws Exception {
    
    
        try {
    
    
            sftp.cd(directory);
            sftp.get(downloadFile, new FileOutputStream(saveFile));
            System.out.println("download file " + directory + "/" + downloadFile + " from host " + sshSession.getHost());
        } catch (SftpException e) {
    
    
            e.printStackTrace();
        } catch (FileNotFoundException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            disconnect();
        }
    }

    /**
     * 下载文件
     *
     * @param src
     * @param dst
     * @throws Exception
     */
    @SuppressWarnings("unchecked")
    public void downloadDir(String src, File dst) throws Exception {
    
    
        try {
    
    
            sftp.cd(src);
        } catch (Exception e) {
    
    
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        dst.mkdirs();

        Vector<ChannelSftp.LsEntry> files = sftp.ls(src);
        for (ChannelSftp.LsEntry lsEntry : files) {
    
    
            if (lsEntry.getFilename().equals(".") || lsEntry.getFilename().equals("..")) {
    
    
                continue;
            }
            if (lsEntry.getLongname().startsWith("d")) {
    
    
                downloadDir(src + "/" + lsEntry.getFilename(), new File(dst, lsEntry.getFilename()));
            } else {
    
    
                download(src, lsEntry.getFilename(), new File(dst, lsEntry.getFilename()));
            }

        }
    }

    /**
     * 删除文件
     *
     * @param directory
     * @param deleteFile
     * @throws SftpException
     */
    public void delete(String directory, String deleteFile) throws SftpException {
    
    
        sftp.cd(directory);
        sftp.rm(deleteFile);
    }

    /**
     * 列出目录下的文件
     *
     * @param directory
     * @return
     * @throws SftpException
     */
    public Vector listFiles(String directory) throws SftpException {
    
    
        return sftp.ls(directory);
    }

    public Session getSshSession() {
    
    
        return sshSession;
    }

    public ChannelSftp getSftp() {
    
    
        return sftp;
    }

}



这个使用比较简单 也没有什么歧义 使用示例就不再写了


sftp按时间过滤文件列表

该方法是受smb1.0中过滤方式的启发 并研究Sftp工具包api 编写的

我们可以看到 ChannelSftp中的ls(path)方法,默认是把所有的entry加入到Vector中,并调用了 ls(path,selector) 方法
在这里插入图片描述
所以我们可以看看entry里面到底有什么:
在这里插入图片描述
filename:文件名 longname:长文件名

再看看attrs里面是什么:

在这里插入图片描述
熟悉linux的同学 能很清楚知道mtime是什么 当然即使不熟悉 也能猜出是modifyTime修改时间的缩写, 既然提供了这些参数 那么我们可以稍加判断, 把符合条件的entry存入Vector即可

代码有点不符合我们的习惯,sftp(ls,selector)方法是没有返回值的,是在外部定义的vector中接收值,且mtime单位是秒 并不是毫秒 这一细节一度让博主怀疑人生。

代码如下:

(需要将代码复制到SftpUtils类中)

 /**
     * 列出(文件的最新修改时间)时间点之后的文件
     *
     * @param directory
     * @return
     * @throws SftpException
     */
    public Vector listFilesLaterThenTime(String directory, long time) throws SftpException {
    
    

        Vector vector = new Vector();

        sftp.ls(directory, new ChannelSftp.LsEntrySelector() {
    
    

            public int select(ChannelSftp.LsEntry entry) {
    
    
				// 很坑的点 atime和mtime的单位是秒 并非我们常用的毫秒
				// 从 getMtimeString()方法中的 mtime * 1000L 可以看出
                long mTime = entry.getAttrs().getMTime() * 1000L;
                if (mTime < time) {
    
    

                    vector.addElement(entry);
                }
                return 0;
            }
        });

        return vector;
    }

sftp注意事项

注: ftp和sftp是有区别的 ,linux中 ftp默认21端口 sftp默认是22端口,sftp传输加密 安全 效率较低,ftp传输不安全 效率较高, 本文讨论的是sftp 两种协议jar包引用不同。


java smb1.0传输

依赖:


        <!-- samba 共享文件夹 -->
        <dependency>
            <groupId>jcifs</groupId>
            <artifactId>jcifs</artifactId>
            <version>1.3.17</version>
        </dependency>

简单封装了一个model类

model类代码:

/**
 * smb传输 model类
 * SMB地址写法:smb://用户名:密码@远程ip/地址/(或有无文件名)(注:如只获取文件夹后缀一定有“/”)
 * <p>
 */
@Data
@EqualsAndHashCode
public class SMBConfig implements Serializable {
    
    
    /**
     * 服务器ip
     */
    private String smbIp;

    /**
     * 服务器账号
     */
    private String smbUser;

    /**
     * 服务器密码
     */
    private String smbPwd;

    /**
     * 文件路径 在共享文件夹中的路径 (不带计算机名称)
     */
    private String smbPath;

    /**
     * 文件名
     */
    private String smbFile;

    /**
     * 输入流
     */
    private InputStream is;

    /**
     * 输出流
     */
    private OutputStream os;

    public SMBConfig() {
    
    

    }

}

核心代码:


// import SMBConfig类路径;
import jcifs.UniAddress;
import jcifs.smb.NtlmPasswordAuthentication;
import jcifs.smb.SmbFile;
import jcifs.smb.SmbSession;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.io.*;

@Component
@Slf4j
public class SMBUtils {
    
    

    /**
     * 往samba上传文件
     *
     * @param url       服务器IP地址
     * @param userName  用户登录名
     * @param password  用户登录密码
     * @param storePath 服务器文件存储路径
     * @param fileName  服务器文件存储名称
     * @param is        文件输入流
     * @return true:上传成功
     * <p>
     * false:上传失败
     */
    public static boolean storeFile(String url, String userName, String password, String storePath, String fileName, InputStream is) {
    
    
        BufferedInputStream bf = null;
        OutputStream os = null;
        boolean result = false;
        try {
    
    
            UniAddress ua = UniAddress.getByName(url);
            NtlmPasswordAuthentication auth = new NtlmPasswordAuthentication(url, userName, password);
            SmbSession.logon(ua, auth);
            SmbFile sf = new SmbFile("smb://" + url + storePath + "/" + fileName, auth);
            sf.connect();
            SmbFile sffolder = new SmbFile("smb://" + url + storePath, auth);
            if (!sffolder.exists())
                sffolder.mkdirs();
            bf = new BufferedInputStream(is);
            os = sf.getOutputStream();
            byte[] bt = new byte[1024];
            int n = bf.read(bt);
            while (n != -1) {
    
    
                os.write(bt, 0, n);
                os.flush();
                n = bf.read(bt);
            }
            result = true;
        } catch (Exception e) {
    
    
            log.error(e.getMessage(), e);
        } finally {
    
    
            try {
    
    
                if (bf != null)
                    bf.close();
                if (os != null)
                    os.close();
            } catch (IOException e) {
    
    
                log.error(e.getMessage(), e);
            }
        }
        return result;
    }

    /**
     * 从samba下载文件
     *
     * @param url        服务器IP地址
     * @param userName   用户登录名
     * @param password   用户登录密码
     * @param remotePath 服务器文件存储路径
     * @param fileName   服务器文件存储名称
     * @param os         文件输出流
     * @return true:下载成功
     * <p>
     * false:下载失败
     */
    public static boolean retrieveFile(String url, String userName, String password, String remotePath, String fileName, OutputStream os) {
    
    
        boolean result = false;
        BufferedInputStream bf = null;
        try {
    
    
            UniAddress ua = UniAddress.getByName(url);
            NtlmPasswordAuthentication auth = new NtlmPasswordAuthentication(url, userName, password);
            SmbSession.logon(ua, auth);
            SmbFile sf = new SmbFile("smb://" + url + remotePath + "/" + fileName, auth);
            if (sf.exists()) {
    
    
                bf = new BufferedInputStream(sf.getInputStream());
                byte[] bt = new byte[1024];
                int n = bf.read(bt);
                while (n != -1) {
    
    
                    os.write(bt, 0, n);
                    os.flush();
                    n = bf.read(bt);
                }
                result = true;
            }
        } catch (Exception e) {
    
    
            log.error(e.getMessage(), e);
        } finally {
    
    
            try {
    
    
                if (bf != null)
                    bf.close();
                if (os != null)
                    os.close();
            } catch (IOException e) {
    
    
                log.error(e.getMessage(), e);
            }
        }
        return result;
    }

    /**
     * 从samba删除文件
     *
     * @param url        服务器IP地址
     * @param userName   用户登录名
     * @param password   用户登录密码
     * @param remotePath 服务器文件存储路径
     * @param fileName   服务器文件存储名称
     * @return true:删除成功
     * <p>
     * false:删除失败
     */
    public static boolean deleteFile(String url, String userName, String password, String remotePath, String fileName) {
    
    
        boolean result = false;
        try {
    
    
            UniAddress ua = UniAddress.getByName(url);
            NtlmPasswordAuthentication auth = new NtlmPasswordAuthentication(url, userName, password);
            SmbSession.logon(ua, auth);
            SmbFile sf = new SmbFile("smb://" + url + remotePath + "/" + fileName, auth);
            sf.delete();
            result = true;
        } catch (Exception e) {
    
    
            log.error(e.getMessage(), e);
        }
        return result;
    }

    /**
     * 往samba上传文件
     *
     * @param smbConfig
     * @return
     */
    public static boolean storeFile(SMBConfig smbConfig) {
    
    
        return storeFile(smbConfig.getSmbIp(), smbConfig.getSmbUser(), smbConfig.getSmbPwd(), smbConfig.getSmbPath(), smbConfig.getSmbFile(), smbConfig.getIs());
    }

    /**
     * 从samba下载文件
     *
     * @param smbConfig
     * @return
     */
    public static boolean retrieveFile(SMBConfig smbConfig) {
    
    
        return retrieveFile(smbConfig.getSmbIp(), smbConfig.getSmbUser(), smbConfig.getSmbPwd(), smbConfig.getSmbPath(), smbConfig.getSmbFile(), smbConfig.getOs());
    }


}

使用示例:

	   SMBConfig smbConfig = new SMBConfig();
 
        smbConfig.setSmbIp("192.168.1.1");
        smbConfig.setSmbPwd("123456");
        smbConfig.setSmbUser("admin");
        // 路径
        smbConfig.setSmbPath("/test");
        // 上传后的文件名叫这个
        smbConfig.setSmbFile("pom");
        // 将springboot项目中根目录的pom.xml文件上传
        smbConfig.setIs(new FileInputStream("pom.xml"));
        // 上传
        storeFile(smbConfig);

注: smbPath 例如在共享文件夹中 路径为test 就写/test ,不用加上下图马赛克位置的计算机名
在这里插入图片描述


smb1.0按时间过滤文件列表

举个场景示例: 服务器上面有成千上万 甚至更多的文件,我们每过一段时间客户端去请求处理一次,那么下次请求 客户端就无需去处理之前已经处理过的文件了。

所以每次请求 需要记录这批文件中 最新的那个修改时间time, 下次去处理修改时间大于time的即可。

观察SmbFile类中的api,发现listFiles中有个SmbFileFilter参数,意味着它支持过滤器,而这个过滤器是个interface接口,说明我们可以自定义实现该接口。
在这里插入图片描述

代码如下(该方法可以复制进SMBUtils类中):

    /**
     * 大于某个时间点的文件
     *
     * @param smbConfig
     * @param time      时间节点
     * @return
     */
    public static List<SmbFile> filesLaterThenTime(SMBConfig smbConfig, long time) {
    
    
        List<SmbFile> res = new ArrayList<>();

        try {
    
    
            UniAddress ua = UniAddress.getByName(smbConfig.getSmbIp());
            NtlmPasswordAuthentication auth = new NtlmPasswordAuthentication(smbConfig.getSmbIp(),
                    smbConfig.getSmbUser(), smbConfig.getSmbPwd());
            SmbSession.logon(ua, auth);
            SmbFile sf = new SmbFile("smb://" + smbConfig.getSmbIp() + "/test" + "/", auth);
            sf.connect();
            SmbFile sffolder = new SmbFile("smb://" + smbConfig.getSmbIp() + "/test" + "/", auth);
			// 设置时间
            SmbLaterThenTimeFileFilter.time.set(time);
            if (sffolder.exists()) {
    
    
            	// 传入自定义过滤器
                SmbFile[] smbFiles = sffolder.listFiles(new SmbLaterThenTimeFileFilter());
                for (SmbFile smbFile : smbFiles) {
    
    
                    res.add(smbFile);
                }
            }
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
        return res;
    }

熟悉filter的同学 看到accept方法 就应该知道怎么回事了

import jcifs.smb.SmbException;
import jcifs.smb.SmbFile;
import jcifs.smb.SmbFileFilter;

import java.util.concurrent.atomic.AtomicLong;

/**
 * 过滤器:最新修改时间大于某个时间点
 *
 * @author qkj
 */
public class SmbLaterThenTimeFileFilter implements SmbFileFilter {
    
    
    public static AtomicLong time = new AtomicLong();

    @Override
    public boolean accept(SmbFile smbFile) throws SmbException {
    
    

        // 最后修改时间大于time(也可以替换成自己要的逻辑)
        return smbFile.getLastModified() > time.get();
    }


}

smb注意事项

smb1.0和2.0方式有差异 依赖也不同,需要确认smb1.0在windows服务器中是否开启。

win10/win11中 在启用或关闭windows功能中开启smb1.0 :
在这里插入图片描述

windows server中 在powerShell用命令开启smb1.0:

 Set-SmbServerConfiguration -EnableSMB1Protocol $true

下面是操作系统的smb默认协议对照图(供参考):

在这里插入图片描述

如果windows服务器没有账号密码,需要先设置;

如果共享功能没有开启 需要先开启
在这里插入图片描述

java smb2.0/3.0传输(smbj方式)

smbj包人气相对较高,目前也没宣布停止维护,但是博主询问过smbj的作者 得到回复是无法从api层面根据创建、修改等时间去过滤,如果没有时间等条件过滤需求(或者允许将文件列表查出 在本地通过代码去过滤) 可以考虑用该方式

2.0/3.0的上传、下载、列表功能,代码也简单给出:

依赖:

        <!-- smb2.0/3.0 -->
        <dependency>
            <groupId>com.hierynomus</groupId>
            <artifactId>smbj</artifactId>
            <!-- 可以在github中 自行查看当前最新版本-->
            <version>0.11.5</version>
        </dependency>

import com.hierynomus.msdtyp.AccessMask;
import com.hierynomus.msfscc.FileAttributes;
import com.hierynomus.msfscc.fileinformation.FileIdBothDirectoryInformation;
import com.hierynomus.mssmb2.SMB2CreateDisposition;
import com.hierynomus.mssmb2.SMB2CreateOptions;
import com.hierynomus.mssmb2.SMB2ShareAccess;
import com.hierynomus.smbj.SMBClient;
import com.hierynomus.smbj.auth.AuthenticationContext;
import com.hierynomus.smbj.connection.Connection;
import com.hierynomus.smbj.session.Session;
import com.hierynomus.smbj.share.DiskShare;
import com.hierynomus.smbj.share.File;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;

@Slf4j
public class SMB2Utils {
    
    


    /**
     * SMB下载文件.
     *
     * @param remoteIp  远程IP.
     * @param remoteUrl 远程地址.
     * @param username  远程用户.
     * @param password  远程密码.
     * @param filename  远程文件.
     * @return 共享文件.
     */
    public static byte[] smbDownload(String remoteIp, String remoteUrl, String username, String password,
                                     String filename) {
    
    
        File shareFile = null;
        InputStream shareIs = null;
        ByteArrayOutputStream shareBaos = null;
        SMBClient client = null;
        Session session = null;
        DiskShare diskShare = null;
        Connection connection = null;
        AuthenticationContext authenticationContext = null;

        try {
    
    
            client = new SMBClient();
            connection = client.connect(remoteIp);
            // 创建连接会话.
            authenticationContext = new AuthenticationContext(username, password.toCharArray(), null);
            session = connection.authenticate(authenticationContext);
            // 操作共享文件.
            diskShare = (DiskShare) session.connectShare(remoteUrl);
            // 获取文件流.上传文件.
            shareFile = diskShare.openFile(filename, EnumSet.of(AccessMask.GENERIC_READ),
                    EnumSet.of(FileAttributes.FILE_ATTRIBUTE_NORMAL), SMB2ShareAccess.ALL,
                    SMB2CreateDisposition.FILE_OPEN_IF, EnumSet.noneOf(SMB2CreateOptions.class));
            shareIs = shareFile.getInputStream();
            // 读取文件并下载.
            shareBaos = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            while (shareIs.read(buffer) != -1) {
    
    
                shareBaos.write(buffer);
                buffer = new byte[1024];
            }
            return shareBaos.toByteArray();
        } catch (Exception ex) {
    
    
            log.error(ex.getMessage());
            return null;
        } finally {
    
    
            try {
    
    
                if (null != shareIs) {
    
    
                    shareIs.close();
                }
                if (null != diskShare) {
    
    
                    diskShare.close();
                }
                if (null != session) {
    
    
                    session.close();
                }
                if (null != client) {
    
    
                    client.close();
                }
            } catch (IOException ex) {
    
    
                log.error(ex.getMessage());
                return null;
            }
        }
    }

    /**
     * SMB上传文件.
     *
     * @param remoteIp  远程IP.
     * @param remoteUrl 远程地址.
     * @param username  远程用户.
     * @param password  远程密码.
     * @param filename  远程文件.
     * @param fileBytes 待上传文件.
     * @return 上传结果.
     */
    public static boolean smbUpload(String remoteIp, String remoteUrl, String username, String password,
                                    String filename, byte[] fileBytes) {
    
    
        File shareFile = null;
        OutputStream shareOs = null;
        SMBClient client = null;
        Session session = null;
        DiskShare diskShare = null;
        Connection connection = null;
        AuthenticationContext authenticationContext = null;

        try {
    
    
            client = new SMBClient();
            connection = client.connect(remoteIp);
            // 创建连接会话.
            authenticationContext = new AuthenticationContext(username, password.toCharArray(), null);
            session = connection.authenticate(authenticationContext);
            // 操作共享文件.
            diskShare = (DiskShare) session.connectShare(remoteUrl);
            // 获取文件流.上传文件.
            shareFile = diskShare.openFile(filename, EnumSet.of(AccessMask.GENERIC_WRITE),
                    EnumSet.of(FileAttributes.FILE_ATTRIBUTE_NORMAL), SMB2ShareAccess.ALL,
                    SMB2CreateDisposition.FILE_OPEN_IF, EnumSet.noneOf(SMB2CreateOptions.class));
            shareOs = shareFile.getOutputStream();
            shareOs.write(fileBytes);
            return true;
        } catch (Exception ex) {
    
    
            log.error(ex.getMessage());
            return false;
        } finally {
    
    
            try {
    
    

                if (null != shareOs) {
    
    
                    shareOs.close();
                }
                if (null != shareFile) {
    
    
                    shareFile.close();
                }
                if (null != diskShare) {
    
    
                    diskShare.close();
                }
                if (null != session) {
    
    
                    session.close();
                }
                if (null != client) {
    
    
                    client.close();
                }
            } catch (IOException ex) {
    
    
                log.error(ex.getMessage());
                return false;
            }
        }
    }

    /**
     * SMB删除文件.
     *
     * @param remoteIp  远程IP.
     * @param remoteUrl 远程地址.
     * @param username  远程用户.
     * @param password  远程密码.
     * @param filename  远程文件.
     * @return 删除结果.
     */
    public static boolean smbDelete(String remoteIp, String remoteUrl, String username, String password,
                                    String filename) {
    
    
        SMBClient client = null;
        Session session = null;
        DiskShare diskShare = null;
        Connection connection = null;
        AuthenticationContext authenticationContext = null;

        try {
    
    
            client = new SMBClient();
            connection = client.connect(remoteIp);
            // 创建连接会话.
            authenticationContext = new AuthenticationContext(username, password.toCharArray(), null);
            session = connection.authenticate(authenticationContext);
            // 操作共享文件.
            diskShare = (DiskShare) session.connectShare(remoteUrl);
            // 获取文件流.上传文件.
            diskShare.rm(filename);
            return true;
        } catch (Exception ex) {
    
    
            log.error(ex.getMessage());
            return false;
        } finally {
    
    
            try {
    
    
                if (null != diskShare) {
    
    
                    diskShare.close();
                }
                if (null != session) {
    
    
                    session.close();
                }
                if (null != client) {
    
    
                    client.close();
                }
            } catch (IOException ex) {
    
    
                log.error(ex.getMessage());
                return false;
            }
        }
    }

    /**
     * SMB查看文件列表.
     *
     * @param remoteIp  远程IP.
     * @param remoteUrl 远程地址.
     * @param username  远程用户.
     * @param password  远程密码.
     * @return 结果.
     */
    public static List<FileIdBothDirectoryInformation> smbList(String remoteIp, String remoteUrl, String username, String password) {
    
    
        SMBClient client = null;
        Session session = null;
        DiskShare diskShare = null;
        Connection connection = null;
        AuthenticationContext authenticationContext = null;

        try {
    
    
            client = new SMBClient();
            connection = client.connect(remoteIp);
            // 创建连接会话.
            authenticationContext = new AuthenticationContext(username, password.toCharArray(), null);
            session = connection.authenticate(authenticationContext);
            // 操作共享文件.
            diskShare = (DiskShare) session.connectShare(remoteUrl);

            // 查看文件列表(在已知remoteUrl的情况下 list的path参数为 "/" )
            List<FileIdBothDirectoryInformation> list = diskShare.list("/");
            return list;
        } catch (Exception ex) {
    
    
            log.error(ex.getMessage());
        } finally {
    
    
            try {
    
    
                if (null != diskShare) {
    
    
                    diskShare.close();
                }
                if (null != session) {
    
    
                    session.close();
                }
                if (null != client) {
    
    
                    client.close();
                }
            } catch (IOException ex) {
    
    
                log.error(ex.getMessage());
            }
        }
        return new ArrayList<>();
    }

	// 测试
    public static void main(String[] args) throws Exception {
    
    
        String remoteIp = "192.168.1.1";
        String remoteUrl = "test";
        String username = "xxx";
        String password = "123456";
        String filename = "demo.png";
        byte[] fileBytes = FileUtils.readFileToByteArray(new java.io.File("pom.xml"));
        // 上传文件.
        SMB2Utils.smbUpload(remoteIp, remoteUrl, username, password, filename, fileBytes);
        // 下载文件.
        byte[] downBytes = SMB2Utils.smbDownload(remoteIp, remoteUrl, username, password, filename);
        System.out.println(downBytes.length);
        // 删除文件.
        SMB2Utils.smbDelete(remoteIp, remoteUrl, username, password, filename);
    }
}


java smb2.0传输(jcifs-ng方式)

jcifs-ng方式,和jcifs类似,也可以针对时间过滤 大体和之前没有区别,但缺点是jcifs-ng 已经宣布停止维护了 (smbj也是老项目了 其实改动概率都不是很大), 而且网上jcifs-ng资料很少,遇到问题需要自己有一定能力去解决

在这里插入图片描述

默认支持1.0-2.0,如果3.0 可能需要自行调整。

依赖:(需要将上文smb1中的jcifs依赖排除)

        <!-- smb1.0/2.0/部分3.0 方式二 -->
        <dependency>
            <groupId>eu.agno3.jcifs</groupId>
            <artifactId>jcifs-ng</artifactId>
            <version>2.1.9</version>
        </dependency>

代码:

  
import com.qiuhuanhen.springroot.domain.model.SMBConfig;
import jcifs.CIFSContext;
import jcifs.context.SingletonContext;
import jcifs.smb.NtlmPasswordAuthenticator;
import jcifs.smb.SmbFile;
import jcifs.smb.SmbFileInputStream;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

/**
 * @author qkj
 */
 
@Slf4j
public class SMB23Utils {
    
    
 
    
    public static void main(String[] args) throws Exception {
    
    

        CIFSContext context = withNTLMCredentials(SingletonContext.getInstance());

        SMBConfig smbConfig = new SMBConfig();
        smbConfig.setSmbIp("192.168.1.1");
        smbConfig.setSmbPwd(PASSWORD);
        smbConfig.setSmbUser(USERNAME);
        smbConfig.setSmbPath("/test");
        smbConfig.setSmbFile("1.txt");
        smbConfig.setOs(new FileOutputStream("test1.txt"));
//        smbConfig.setIs(new FileInputStream("1.txt"));
        List<SmbFile> files = filesLaterThenTime(smbConfig,context,1676962027799L);
        retrieveFile(smbConfig, context);
//        storeFile(smbConfig, context);
        context.close();

    }

    /**
     * 列出所有文件
     * @param smbConfig
     * @param context
     * @return
     * @throws Exception
     */
    public static List<SmbFile> files(SMBConfig smbConfig,CIFSContext context) throws Exception {
    
    
        List<SmbFile> res = new ArrayList<>();
        SmbFile sffolder = new SmbFile("smb://" + smbConfig.getSmbIp() + smbConfig.getSmbPath() + "/", context);

        SmbFile[] smbFiles = sffolder.listFiles();
        for (SmbFile smbFile : smbFiles) {
    
    
            res.add(smbFile);
        }
        return res;
    }


    /**
     * 列出某个修改时间点后的文件
     * @param smbConfig
     * @param context
     * @param time
     * @return
     * @throws Exception
     */
    public static List<SmbFile> filesLaterThenTime(SMBConfig smbConfig,CIFSContext context, long time) throws Exception {
    
    
        List<SmbFile> res = new ArrayList<>();
        SmbFile sffolder = new SmbFile("smb://" + smbConfig.getSmbIp() + smbConfig.getSmbPath() + "/", context);

        SmbLaterThenTimeFileFilter.time.set(time);
        SmbFile[] smbFiles = sffolder.listFiles(new SmbLaterThenTimeFileFilter());
        for (SmbFile smbFile : smbFiles) {
    
    
            res.add(smbFile);
        }
        return res;
    }


    /**
     * 往samba上传文件
     *
     * @param smbConfig
     * @return
     */
    public static boolean storeFile(SMBConfig smbConfig, CIFSContext context) {
    
    
        return storeFile(smbConfig.getSmbIp(), context, smbConfig.getSmbPath(), smbConfig.getSmbFile(), smbConfig.getIs());
    }

    /**
     * 从samba下载文件
     *
     * @param smbConfig
     * @return
     */
    public static boolean retrieveFile(SMBConfig smbConfig, CIFSContext context) {
    
    
        return retrieveFile(smbConfig.getSmbIp(), context, smbConfig.getSmbPath(), smbConfig.getSmbFile(), smbConfig.getOs());
    }


    /**
     * 往samba上传文件
     *
     * @param url       服务器IP地址
     * @param storePath 服务器文件存储路径
     * @param fileName  服务器文件存储名称
     * @param is        文件输入流
     * @return true:上传成功
     * <p>
     * false:上传失败
     */
    public static boolean storeFile(String url, CIFSContext context, String storePath, String fileName, InputStream is) {
    
    
        BufferedInputStream bf = null;
        OutputStream os = null;
        boolean result = false;
        SmbFile sf = null;
        try {
    
    
            SmbFile sffolder = new SmbFile("smb://" + url + storePath, context);
            if (!sffolder.exists()) {
    
    
                sffolder.mkdirs();
            }

            sf = new SmbFile("smb://" + url + storePath + "/" + fileName, context);
            sf.connect();

            bf = new BufferedInputStream(is);
            os = sf.getOutputStream();
            byte[] bt = new byte[1024];
            int n = bf.read(bt);
            while (n != -1) {
    
    
                os.write(bt, 0, n);
                os.flush();
                n = bf.read(bt);
            }
            result = true;
        } catch (Exception e) {
    
    
            log.error(e.getMessage(), e);
        } finally {
    
    

            try {
    
    

                if (bf != null)
                    bf.close();
                if (os != null)
                    os.close();
                if (null != sf)
                    sf.close();
            } catch (IOException e) {
    
    
                log.error(e.getMessage(), e);
            }
        }
        return result;
    }

    /**
     * 从samba下载文件
     *
     * @param url        服务器IP地址
     * @param remotePath 服务器文件存储路径
     * @param fileName   服务器文件存储名称
     * @param os         文件输出流
     * @return true:下载成功
     * <p>
     * false:下载失败
     */
    public static boolean retrieveFile(String url, CIFSContext context, String remotePath, String fileName, OutputStream os) {
    
    
        boolean result = false;
        BufferedInputStream bf = null;
        OutputStream bos = new BufferedOutputStream(os);

        InputStream sf = null;
        SmbFile file = null;
        try {
    
    
            file = new SmbFile("smb://" + url + remotePath + "/" + fileName, context);
            file.connect();

            sf = new SmbFileInputStream(file);

            byte[] bt = new byte[1024];
                int n = sf.read(bt);
                while (n != -1) {
    
    
                    os.write(bt, 0, n);
                    os.flush();
                    n = sf.read(bt);
                }

            result = true;
        } catch (IOException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            try {
    
    
                if (null != sf)
                    sf.close();
                if (bos != null) {
    
    
                    bos.close();
                }
                if (os != null)
                    os.flush();
                os.close();
                if (bf != null)

                    bf.close();
                if (null!=file)
                file.close();


            } catch (IOException e) {
    
    
                log.error(e.getMessage(), e);
            }
        }


        return result;
    }
    
    public static CIFSContext withNTLMCredentials(CIFSContext ctx) {
    
    
        return ctx.withCredentials(new NtlmPasswordAuthenticator(
                USERNAME, PASSWORD));
    }

}


model类代码:

/**
 * smb传输 model类
 * SMB地址写法:smb://用户名:密码@远程ip/地址/(或有无文件名)(注:如只获取文件夹后缀一定有“/”)
 * <p>
 */
@Data
@EqualsAndHashCode
public class SMBConfig implements Serializable {
    
    
    /**
     * 服务器ip
     */
    private String smbIp;

    /**
     * 服务器账号
     */
    private String smbUser;

    /**
     * 服务器密码
     */
    private String smbPwd;

    /**
     * 文件路径 在共享文件夹中的路径 (不带计算机名称)
     */
    private String smbPath;

    /**
     * 文件名
     */
    private String smbFile;

    /**
     * 输入流
     */
    private InputStream is;

    /**
     * 输出流
     */
    private OutputStream os;

    public SMBConfig() {
    
    

    }

}

这个代码调用下载方法 在运行结束后会报debug级别的异常以及java.net.SocketException: Socket closed,目前没有发现会影响使用,debug级别的错误 End of file 作者在issue区说没什么问题 它就是代表结束了 ,socket closed或许socket机制本身如此

猜你喜欢

转载自blog.csdn.net/qq_36268103/article/details/129145888