报错jcifs.smb.SmbException: Failed to connect: 0.0.0.0<00>/IP

       问题

         需要访问服务器的nas盘,是smb协议的,用的jsifs包,上网找了方法使用后报错jcifs.smb.SmbException: Failed to connect: 0.0.0.0<00>/IP

       原因

        是jsifs仅支持SMB1,不支持SMB2/SMB3,没办法改服务器协议,所以选择使用smbj包。

        依赖如下

        <dependency>
            <groupId>com.hierynomus</groupId>
            <artifactId>smbj</artifactId>
            <version>0.13.0</version>
        </dependency>

        github仓库:https://github.com/hierynomus/smbj

        代码如下

        下面是根据仓库的demo和网上找的内容自己写了一个小的工具类,不过后面又不用这种方式访问了,所以没有得到充分的测试。

import com.hierynomus.smbj.SMBClient;
import com.hierynomus.smbj.SmbConfig;
import com.hierynomus.smbj.auth.AuthenticationContext;
import com.hierynomus.smbj.connection.Connection;
import com.hierynomus.smbj.session.Session;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

@Configuration
public class NasConfig {
    /**
     * IP地址
     */
    @Value("${nas.hostName}")
    private final static String hostName = "xxx";

    /**
     * 目标用户名
     **/
    @Value("${nas.username}")
    private final static String username = "xxx";

    /**
     * 密码
     **/
    @Value("${nas.password}")
    private final static String password = "xxx";

    /**
     * 超时设置读,写和Transact超时(默认为60秒)
     **/
    @Value("${nas.timeout}")
    private final static int timeout = 120;

    /**
     * Socket超时(默认为0秒)
     **/
    @Value("${nas.soTimeout}")
    private final static int soTimeout = 180;

    @Bean(name = "SmbSession")
    private static Session getSession() {
        SmbConfig config = SmbConfig.builder()
                .withTimeout(timeout, TimeUnit.SECONDS) // 超时设置读,写和Transact超时(默认为60秒)
                .withSoTimeout(soTimeout, TimeUnit.SECONDS) // Socket超时(默认为0秒)
                .build();

        SMBClient client = new SMBClient(config);
        Connection connection = null;
        try {
            connection = client.connect(hostName);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        AuthenticationContext ac = new AuthenticationContext(username, password.toCharArray(), "");
        return connection.authenticate(ac);
    }
}


import com.hierynomus.msdtyp.AccessMask;
import com.hierynomus.msfscc.fileinformation.FileIdBothDirectoryInformation;
import com.hierynomus.mssmb2.SMB2CreateDisposition;
import com.hierynomus.mssmb2.SMB2ShareAccess;
import com.hierynomus.mssmb2.SMBApiException;
import com.hierynomus.smbj.SMBClient;
import com.hierynomus.smbj.SmbConfig;
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 jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * SMB2CreateDisposition 可选参数及解释
 * 如果该文件已经存在,它取代。 否则,创建该文件。 此值不应该被用于打印机对象
 * FILE_SUPERSEDE(0x00000000L),
 * 如果该文件已经存在,返回成功; 否则,操作失败。 绝不能用于打印机对象
 * FILE_OPEN(0x00000001L),
 *如果该文件已经存在,操作失败; 否则,创建该文件。
 * FILE_CREATE(0x00000002L),
 * 打开该文件,如果它已经存在; 否则,创建该文件。 此值不应该被用于打印机对象
 * FILE_OPEN_IF(0x00000003L),
 * 覆盖该文件,如果它已经存在; 否则,操作失败。 绝不能用于打印机对象。
 * FILE_OVERWRITE(0x00000004L),
 * 覆盖该文件,如果它已经存在; 否则,创建该文件。 此值不应该被用于打印机对象。
 */
@Component
@Slf4j
public class NasUtils {

    @Resource(name = "SmbSession")
    private Session session;



    /**
     * 列出指定SMB共享中路径下的文件,根据搜索模式进行过滤,并返回文件列表
     *
     * @param shareName     SMB共享的名称,例如 "\\\\server\\share"
     * @param path          在共享内的路径,例如 "documents"
     * @param searchPattern 文件名匹配模式,如 "*.txt" 用于查找所有文本文件
     * @return 文件信息列表,包含匹配搜索模式的所有文件
     */
    public List<FileIdBothDirectoryInformation> listFiles(String shareName, String path, String searchPattern) {
        try (DiskShare share = (DiskShare) session.connectShare(shareName)) {
            return share.list(path, searchPattern);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 从NAS读取指定文件的内容并转为String
     *
     * @param shareName 网络共享文件夹的名称
     * @param path 文件在共享文件夹中的路径
     * @param fileName 要读取的文件名
     * @return 文件内容的字符串表示
     */
    public String readFileFromNas(String shareName, String path, String fileName) {
        try (DiskShare share = (DiskShare) session.connectShare(shareName)) {
            File file = share.openFile(shareName + java.io.File.separator + path + java.io.File.separator + fileName,
                    new HashSet<>(List.of(AccessMask.GENERIC_ALL)),
                    null,
                    SMB2ShareAccess.ALL,
                    SMB2CreateDisposition.FILE_OPEN,
                    null);

            StringBuilder content = new StringBuilder();
            try (InputStream inputStream = file.getInputStream();
                 BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
                 InputStreamReader inputStreamReader = new InputStreamReader(bufferedInputStream, StandardCharsets.UTF_8); // 使用合适的字符编码
                 BufferedReader reader = new BufferedReader(inputStreamReader)) {
                String currentLine;
                while ((currentLine = reader.readLine()) != null) {
                    content.append(currentLine);
                }
                return content.toString();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }

        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    /**
     * 上传文件到NAS
     *
     * @param shareName NAS共享文件夹名
     * @param path      目标路径
     * @param fileName  文件名
     * @param localPath 本地文件路径
     * @return 如果上传成功返回true,否则返回false
     */
    public boolean uploadFileToNas(String shareName, String path, String fileName, String localPath) {
        try (DiskShare share = (DiskShare) session.connectShare(shareName);
             BufferedInputStream bis = new BufferedInputStream(new FileInputStream(localPath + fileName))) {
            File f = null;
            // 文件不存在则创建,存在则打开。
            try {
                System.out.println(path + java.io.File.separator + fileName);
                f = share.openFile(shareName + java.io.File.separator + path + java.io.File.separator + fileName, new HashSet<>(
                                List.of(AccessMask.GENERIC_ALL)),
                        null, SMB2ShareAccess.ALL, SMB2CreateDisposition.FILE_CREATE,
                        null);
            } catch (SMBApiException e) {
                // 检查异常是否表示文件已存在
                if (e.getMessage().contains("STATUS_OBJECT_NAME_COLLISION")) {
                    System.out.println("文件已经存在执行打开操作");
                    System.out.println(path + java.io.File.separator + fileName);
                    f = share.openFile(shareName + java.io.File.separator + path + java.io.File.separator + fileName, new HashSet<>(
                                    List.of(AccessMask.GENERIC_ALL)),
                            null, SMB2ShareAccess.ALL, SMB2CreateDisposition.FILE_OVERWRITE,
                            null);
                } else {
                    log.error(e.getMessage());
                    return false;
                }
            }

            if (f != null) {
                OutputStream outputStream = f.getOutputStream();
                byte[] buffer = new byte[1024];
                int len = 0;
                while ((len = bis.read(buffer, 0, buffer.length)) != -1) {
                    outputStream.write(buffer, 0, len);
                }
                outputStream.flush();
                outputStream.close();
                System.out.println("上传了文件");
                return true;
            } else {
                return false;
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 从NAS下载单个文件
     *
     * @param shareName 共享名
     * @param path      NAS上的文件路径
     * @param fileName  文件名
     * @param localPath 本地保存路径
     */
    public void downloadFileFromNas(String shareName, String path, String fileName, String localPath) {
        // 构建NAS上的文件完整路径
        String filePath = shareName + java.io.File.separator + path + java.io.File.separator + fileName;
        // 构建本地保存的文件路径
        String dstPath = localPath + java.io.File.separator + fileName;

        try (DiskShare share = (DiskShare) session.connectShare(shareName);
             BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(dstPath))) {

            // 检查文件是否存在
            if (share.fileExists(filePath)) {
                System.out.println("正在下载文件:" + filePath);

                // 打开NAS上的文件并获取输入流
                File smbFileRead = share.openFile(filePath, EnumSet.of(AccessMask.GENERIC_READ), null,
                        SMB2ShareAccess.ALL, SMB2CreateDisposition.FILE_OPEN, null);
                InputStream in = smbFileRead.getInputStream();

                // 读取并写入文件内容
                byte[] buffer = new byte[4096];
                int len = 0;
                while ((len = in.read(buffer, 0, buffer.length)) != -1) {
                    bos.write(buffer, 0, len);
                }

                System.out.println("文件下载成功");
                System.out.println("==========================");
            } else {
                System.out.println("文件不存在");
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 从NAS的指定路径下载匹配指定模式的文件
     *
     * @param shareName     共享文件夹名
     * @param path          NAS上的目录路径
     * @param localPath     本地保存路径
     * @param searchPattern 文件搜索模式
     */
    public void downloadAllFilesFromNas(String shareName, String path, String localPath, String searchPattern) {

        try (DiskShare share = (DiskShare) session.connectShare(shareName)) {
            // 遍历NAS上匹配的文件
            for (FileIdBothDirectoryInformation f : share.list(path, searchPattern)) {
                // 构建NAS上的完整文件路径
                String filePath = shareName + path + f.getFileName();
                // 构建本地保存的文件路径
                String dstPath = localPath + f.getFileName();

                // 创建本地文件输出流
                BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(dstPath));

                // 检查文件是否存在
                if (share.fileExists(filePath)) {
                    System.out.println("正在下载文件:" + f.getFileName());

                    // 打开NAS上的文件并获取输入流
                    File smbFileRead = share.openFile(filePath, EnumSet.of(AccessMask.GENERIC_READ), null,
                            SMB2ShareAccess.ALL, SMB2CreateDisposition.FILE_OPEN, null);
                    InputStream in = smbFileRead.getInputStream();

                    // 读取并写入文件内容
                    byte[] buffer = new byte[4096];
                    int len = 0;
                    while ((len = in.read(buffer, 0, buffer.length)) != -1) {
                        bos.write(buffer, 0, len);
                    }

                    bos.flush();
                    bos.close();

                    System.out.println("文件下载成功");
                    System.out.println("==========================");
                } else {
                    System.out.println("文件不存在");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 删除指定共享目录下的文件
     *
     * @param shareName 共享目录名称
     * @param path      文件路径
     */
    public void deleteFile(String shareName, String path, String fileName) {
        try (DiskShare share = (DiskShare) session.connectShare(shareName)) {
            share.rm(path + fileName);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

除了GitHub,还可以参考的文章:

Java使用SMBJ包从仅支持SMB2/SMB3协议的共享文件夹下载文件_smbj下载-CSDN博客

Java 操作Windows10+ 共享文件 SMBJ库的基本使用 - 代码先锋网

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值