C#连接使用共享目录(Cifs/Smb)的辅助类

  接上一篇,上一篇说道.NetCore上使用Ftp的一些建议,主要是希望我们的应用对Ftp不要形成强依赖,因为我们还有其他的替代方案,比如Cifs/Smb

  本文给出我自己封装使用的两个辅助类,基础功能类似(代码运行环境.Net6)

  CifsClient

  第一个辅助类是基于包SharpCifs.Std,可以通过Nuget安装,支持跨平台,包源码地址:https://github.com/ume05rw/SharpCifs.Std

  辅助类:

CifsClient
    public class CifsClient
    {
        string workingDirectory = "";
        string seperator = "/";

        public CifsClient(string host, string shareName)
        {
            Host = host;
            ShareName = shareName;
        }

        /// <summary>
        /// Host
        /// </summary>
        public string Host { get; }
        /// <summary>
        /// 名称
        /// </summary>
        public string ShareName { get; set; }
        /// <summary>
        /// 端口(默认445)
        /// </summary>
        public int? Port { get; set; }
        /// <summary>
        /// 域
        /// </summary>
        public string Domain { get; set; } = string.Empty;
        /// <summary>
        /// 账号
        /// </summary>
        public string? User { get; set; }
        /// <summary>
        /// 密码
        /// </summary>
        public string? Password { get; set; }
        /// <summary>
        /// 当前工作目录
        /// </summary>
        public string WorkingDirectory => workingDirectory;

        /// <summary>
        /// 设置工作目录
        /// </summary>
        /// <param name="path"></param>
        /// <param name="absolute"></param>
        public void SetWorkingDirectory(string path)
        {
            if (IsAbsolute(path))
            {
                workingDirectory = FormatPath("", path);
            }
            else
            {
                workingDirectory = FormatPath(workingDirectory, path);
            }
        }
        /// <summary>
        /// 获取指定目录下的所有文件
        /// </summary>
        /// <param name="remoteFile"></param>
        /// <returns></returns>
        public async Task<string[]> GetFiles(string remoteFile)
        {
            var folder = MakeSmbFile(remoteFile, true);
            var files = await folder.ListFilesAsync();
            return files.Where(f => f.IsFile()).Select(f => GetName(f)).Where(f => !string.IsNullOrEmpty(f)).ToArray();
        }
        /// <summary>
        /// 获取指定目录下的所有目录
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public async Task<string[]> GetDirectories(string path)
        {
            var folder = MakeSmbFile(path, true);
            var files = await folder.ListFilesAsync();
            return files.Where(f => f.IsDirectory()).Select(f => GetName(f)).Where(f => !string.IsNullOrEmpty(f)).ToArray();
        }
        /// <summary>
        /// 获取指定目录下的所有目录及文件
        /// </summary>
        /// <param name="path"></param>
        /// <param name="pattern"></param>
        /// <returns></returns>
        public async Task<CifsClientFile[]> GetList(string path)
        {
            var folder = MakeSmbFile(path, true);
            var files = await folder.ListFilesAsync();

            var cifsClientFileList = new List<CifsClientFile>();
            foreach (var file in files)
            {
                var name = GetName(file);
                if (!string.IsNullOrEmpty(name))
                {
                    cifsClientFileList.Add(new CifsClientFile(name!, file.IsDirectory(), file.GetLocalLastModified(), file.GetLocalCreateTime()));
                }
            }

            return cifsClientFileList.ToArray();
        }
        /// <summary>
        /// 创建目录
        /// </summary>
        /// <param name="path"></param>
        /// <param name="rescure"></param>
        /// <returns></returns>
        public async Task CreateDirectory(string path, bool rescure = false)
        {
            var folder = MakeSmbFile(path, true);
            if (folder.Exists())
            {
                return;
            }
            if (rescure)
            {
                folder.Mkdirs();
            }
            else
            {
                await folder.MkDirAsync();
            }
        }
        /// <summary>
        /// 删除目录
        /// </summary>
        /// <param name="path"></param>
        public async Task RemoveDirectory(string path)
        {
            var folder = MakeSmbFile(path, true);
            await folder.DeleteAsync();
        }
        /// <summary>
        /// 删除文件
        /// </summary>
        /// <param name="remoteFile"></param>
        public async Task Delete(string remoteFile)
        {
            var file = MakeSmbFile(remoteFile, false);
            await file.DeleteAsync();
        }
        /// <summary>
        /// 目录是否存在
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public bool DirectoryIsExist(string path)
        {
            var folder = MakeSmbFile(path, true);
            return folder.Exists();
        }
        /// <summary>
        /// 文件是否存在
        /// </summary>
        /// <param name="remoteFile"></param>
        /// <returns></returns>
        public bool FileIsExist(string remoteFile)
        {
            var file = MakeSmbFile(remoteFile, false);
            return file.Exists();
        }
        /// <summary>
        /// 上传文件
        /// </summary>
        /// <param name="remoteFile">远程文件名称,带路径</param>
        /// <param name="localFile">本地文件</param>
        public async Task Upload(string remoteFile, string localFile)
        {
            using var fs = File.OpenRead(localFile);
            await Upload(remoteFile, fs);
        }
        /// <summary>
        /// 上传文件
        /// </summary>
        /// <param name="remoteFile">远程文件名称,带路径</param>
        /// <param name="stream">本地文件</param>
        public async Task Upload(string remoteFile, Stream stream)
        {
            var file = MakeSmbFile(remoteFile, false);
            file.CreateNewFile();
            using var writeStream = await file.GetOutputStreamAsync();
            await stream.CopyToAsync(writeStream);
        }
        /// <summary>
        /// 下载文件
        /// </summary>
        /// <param name="remoteFile"></param>
        /// <param name="localFile"></param>
        public async Task Download(string remoteFile, string localFile)
        {
            using var fs = File.OpenWrite(localFile);
            await Download(remoteFile, fs);
        }
        /// <summary>
        /// 下载文件
        /// </summary>
        /// <param name="remoteFile"></param>
        /// <param name="stream"></param>
        public async Task Download(string remoteFile, Stream stream)
        {
            var file = MakeSmbFile(remoteFile, false);
            using var readStream = await file.GetInputStreamAsync();
            await ((Stream)readStream).CopyToAsync(stream);
            if (stream.CanSeek)
            {
                stream.Seek(0, SeekOrigin.Begin);
            }
        }

        private SmbFile MakeSmbFile(string path, bool directory)
        {
            string uri = "smb://";
            uri += Host;
            if (Port != null)
            {
                uri += ":" + Port;
            }
            uri += seperator + ShareName + seperator + FormatPath(workingDirectory, path);
            if (directory)
            {
                uri += seperator;
            }

            if (!string.IsNullOrEmpty(User) && !string.IsNullOrEmpty(Password))
            {
                return new SmbFile(uri, new NtlmPasswordAuthentication(Domain, User, Password));
            }
            else
            {
                return new SmbFile(uri);
            }
        }
        private bool IsAbsolute(string path)
        {
            return path.StartsWith(@"\") || path.StartsWith(@"/");
        }
        private string FormatPath(string workingDirectory, string path)
        {
            List<string> list = new List<string>();
            list.AddRange(path.Split(new string[] { "/", "\\" }, StringSplitOptions.RemoveEmptyEntries));
            //文件名基于Uri,不能识别#等路由字符
            var paths = list.Select(f => HttpUtility.UrlEncode(f));

            //如果是绝对路径,那么直接使用
            if (!IsAbsolute(path))
            {
                paths = paths.Prepend(workingDirectory);
            }
            return string.Join(seperator, paths.Where(f => !string.IsNullOrEmpty(f)));
        }
        private string GetName(SmbFile smbFile)
        {
            //因为smbFile.GetName是由Uri得到,文件名中带#等无法被识别
            return smbFile.GetPath().Split(new string[] { "/", "\\" }, StringSplitOptions.RemoveEmptyEntries).LastOrDefault() ?? string.Empty;
        }
    }
    public class CifsClientFile
    {
        public CifsClientFile(string name, bool isDirectory, DateTime lastModifiedTime, DateTime creationTime)
        {
            Name = name;
            IsDirectory = isDirectory;
            if (lastModifiedTime.Kind == DateTimeKind.Utc)
            {
                lastModifiedTime = lastModifiedTime.ToLocalTime();
            }
            LastModifiedTime = lastModifiedTime;
            if (creationTime.Kind == DateTimeKind.Utc)
            {
                creationTime = creationTime.ToLocalTime();
            }
            CreationTime = creationTime;
        }

        /// <summary>
        /// 名称(不包含目录)
        /// </summary>
        public string Name { get; }
        /// <summary>
        /// 是否是文件
        /// </summary>
        public bool IsDirectory { get; }
        /// <summary>
        /// 最后修改时间
        /// </summary>
        public DateTime LastModifiedTime { get; }
        /// <summary>
        /// 创建时间
        /// </summary>
        public DateTime CreationTime { get; }
    }

  测试代码:  

    public async Task Main(string[] args)
    {
        var value = "hello Cifs";
        using var ms = new MemoryStream(Encoding.UTF8.GetBytes(value));

        CifsClient client = new CifsClient("localhost", "test")
        {
            User = "admin",
            Domain = "",
            Password = "123456",
            Port = 445
        };

        //设置工作目录
        await client.CreateDirectory("RootPath", true);
        client.SetWorkingDirectory("RootPath");

        string filePath = "test&a#1";
        string fileName = Path.Combine(filePath, "test$a#2.txt");

        //目录是否存在
        var result = client.DirectoryIsExist(filePath);
        if (!result)
        {
            //创建目录
            await client.CreateDirectory(filePath, true);
        }

        //文件是否存在
        result = client.FileIsExist(fileName);
        if (result)
        {
            //删除文件
            await client.Delete(fileName);
        }

        //上传文件
        await client.Upload(fileName, ms);

        //获取指定目录下的文件
        var files = await client.GetFiles(filePath);
        //获取指定目录下的子目录
        var directories = await client.GetDirectories("");
        //获取指定目录下的文件及子目录
        var list = await client.GetList(filePath);

        //下载文件
        using var fs = new MemoryStream();
        await client.Download(fileName, fs);

        //删除目录及它下面的所有文件
        await client.RemoveDirectory(filePath);
    }

  SmbClient  

  这个辅助类基于SMBLibrary包,也可以通过Nuget安装,支持跨平台,包源码地址:https://github.com/TalAloni/SMBLibrary

  辅助类:

SmbClient
     public class SmbClient : IDisposable
    {
        bool disposed = false;
        ISMBClient? client;
        ISMBFileStore? store;
        string workingDirectory = "";
        string seperator = "\\";

        public SmbClient(string host, string shareName)
        {
            Host = host;
            ShareName = shareName;
        }

        /// <summary>
        /// Host
        /// </summary>
        public string Host { get; }
        /// <summary>
        /// 名称
        /// </summary>
        public string ShareName { get; }
        /// <summary>
        /// 端口(DirectTCPTransport默认端口445, NetBiosOverTCP默认端口139)
        /// </summary>
        public int Port { get; set; } = 445;
        /// <summary>
        /// 域
        /// </summary>
        public string Domain { get; set; } = string.Empty;
        /// <summary>
        /// 账号
        /// </summary>
        public string? User { get; set; }
        /// <summary>
        /// 密码
        /// </summary>
        public string? Password { get; set; }
        /// <summary>
        /// 是否使用NetBios
        /// </summary>
        public bool NetBiosOverTCP { get; set; }
        /// <summary>
        /// 是否已连接
        /// </summary>
        public bool IsConnected => client?.IsConnected ?? false;
        /// <summary>
        /// 当前工作目录
        /// </summary>
        public string WorkingDirectory => workingDirectory;

        /// <summary>
        /// 设置工作目录
        /// </summary>
        /// <param name="path"></param>
        public void SetWorkingDirectory(string path)
        {
            if (IsAbsolute(path))
            {
                workingDirectory = FormatPath("", path);
            }
            else
            {
                workingDirectory = FormatPath(workingDirectory, path);
            }
        }
        /// <summary>
        /// 开始连接
        /// </summary>
        /// <returns></returns>
        public bool Connect()
        {
            GenerateClient();
            return IsConnected;
        }
        /// <summary>
        /// 获取指定目录下的所有文件
        /// </summary>
        /// <param name="path"></param>
        /// <param name="pattern"></param>
        /// <returns></returns>
        public string[] GetFiles(string path, string pattern = "*")
        {
            CheckDisposed();
            CheckConnected();

            path = FormatPath(workingDirectory, path);
            return GetList(path, pattern, false).Select(f => f.Name).ToArray();
        }
        /// <summary>
        /// 获取指定目录下的所有目录
        /// </summary>
        /// <param name="path"></param>
        /// <param name="pattern"></param>
        /// <returns></returns>
        public string[] GetDirectories(string path, string pattern = "*")
        {
            CheckDisposed();
            CheckConnected();

            path = FormatPath(workingDirectory, path);
            return GetList(path, pattern, true).Select(f => f.Name).ToArray();
        }
        /// <summary>
        /// 获取指定目录下的所有目录及文件
        /// </summary>
        /// <param name="path"></param>
        /// <param name="pattern"></param>
        /// <returns></returns>
        public SmbClientFile[] GetList(string path, string pattern = "*")
        {
            CheckDisposed();
            CheckConnected();

            path = FormatPath(workingDirectory, path);
            return GetList(path, pattern, null);
        }
        /// <summary>
        /// 创建目录
        /// </summary>
        /// <param name="path"></param>
        /// <param name="rescure"></param>
        /// <returns></returns>
        public void CreateDirectory(string path, bool rescure = false)
        {
            CheckDisposed();
            CheckConnected();

            var isAbsolute = IsAbsolute(path);
            var splits = path.Split(new string[] { "\\", "/" }, StringSplitOptions.RemoveEmptyEntries);
            if (!rescure)
            {
                splits = new string[] { string.Join(seperator, splits) };
            }
            var dir = isAbsolute ? "" : workingDirectory;
            foreach (var split in splits)
            {
                dir = FormatPath(dir, split);
                var status = store!.CreateFile(out var directoryHandle, out var _, dir, AccessMask.GENERIC_WRITE, FileAttributes.Directory,
                    ShareAccess.Write, CreateDisposition.FILE_OPEN_IF, CreateOptions.FILE_DIRECTORY_FILE, null);
                CheckStatus(status, nameof(store.CreateFile));

                status = store.CloseFile(directoryHandle);
                CheckStatus(status, nameof(store.CloseFile));
            }
        }
        /// <summary>
        /// 删除目录
        /// </summary>
        /// <param name="path"></param>
        public void RemoveDirectory(string path)
        {
            CheckDisposed();
            CheckConnected();

            var currentPath = FormatPath(workingDirectory, path);
            var items = GetList(currentPath, "*", null);
            foreach (var item in items)
            {
                var name = $"{path}{seperator}{item.Name}";
                if (item.IsDirectory)
                {
                    RemoveDirectory(name);
                }
                else
                {
                    Delete(name);
                }
            }

            var status = store!.CreateFile(out var directoryHandle, out _, currentPath, AccessMask.GENERIC_READ | AccessMask.GENERIC_WRITE | AccessMask.DELETE | AccessMask.SYNCHRONIZE,
                FileAttributes.Normal, ShareAccess.None, CreateDisposition.FILE_OPEN, CreateOptions.FILE_DIRECTORY_FILE, null);
            if (status == NTStatus.STATUS_OBJECT_PATH_NOT_FOUND)
            {
                return;
            }
            CheckStatus(status, nameof(store.CreateFile));

            FileDispositionInformation fileDispositionInformation = new FileDispositionInformation();
            fileDispositionInformation.DeletePending = true;
            status = store.SetFileInformation(directoryHandle, fileDispositionInformation);
            CheckStatus(status, nameof(store.SetFileInformation), path);

            status = store.CloseFile(directoryHandle);
            CheckStatus(status, nameof(store.CloseFile));
        }
        /// <summary>
        /// 删除文件
        /// </summary>
        /// <param name="remoteFile"></param>
        public void Delete(string remoteFile)
        {
            CheckDisposed();
            CheckConnected();

            remoteFile = FormatPath(workingDirectory, remoteFile);
            var status = store!.CreateFile(out var fileHandle, out _, remoteFile, AccessMask.GENERIC_WRITE | AccessMask.DELETE | AccessMask.SYNCHRONIZE,
                FileAttributes.Normal, ShareAccess.None, CreateDisposition.FILE_OPEN, CreateOptions.FILE_NON_DIRECTORY_FILE, null);
            if (status == NTStatus.STATUS_OBJECT_NAME_NOT_FOUND)
            {
                return;
            }
            CheckStatus(status, nameof(store.CreateFile));

            FileDispositionInformation fileDispositionInformation = new FileDispositionInformation();
            fileDispositionInformation.DeletePending = true;
            status = store.SetFileInformation(fileHandle, fileDispositionInformation);
            CheckStatus(status, nameof(store.SetFileInformation));

            status = store.CloseFile(fileHandle);
            CheckStatus(status, nameof(store.CloseFile));
        }
        /// <summary>
        /// 目录是否存在
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public bool DirectoryIsExist(string path)
        {
            CheckDisposed();
            CheckConnected();

            try
            {
                path = FormatPath(workingDirectory, path);
                var status = store!.CreateFile(out var directoryHandle, out var _, path, AccessMask.GENERIC_READ, FileAttributes.Directory,
                    ShareAccess.Read, CreateDisposition.FILE_OPEN, CreateOptions.FILE_DIRECTORY_FILE, null);
                if (status != NTStatus.STATUS_SUCCESS)
                {
                    return false;
                }

                store.CloseFile(directoryHandle);
                return true;
            }
            catch
            {
                return false;
            }
        }
        /// <summary>
        /// 文件是否存在
        /// </summary>
        /// <param name="remoteFile"></param>
        /// <returns></returns>
        public bool FileIsExist(string remoteFile)
        {
            CheckDisposed();
            CheckConnected();

            try
            {
                remoteFile = FormatPath(workingDirectory, remoteFile);
                var status = store!.CreateFile(out var directoryHandle, out var _, remoteFile, AccessMask.GENERIC_READ, FileAttributes.Archive,
                    ShareAccess.Read, CreateDisposition.FILE_OPEN, CreateOptions.FILE_NON_DIRECTORY_FILE, null);
                if (status != NTStatus.STATUS_SUCCESS)
                {
                    return false;
                }

                store.CloseFile(directoryHandle);
                return true;
            }
            catch
            {
                return false;
            }
        }
        /// <summary>
        /// 上传文件
        /// </summary>
        /// <param name="remoteFile"></param>
        /// <param name="localFile"></param>
        public void Upload(string remoteFile, string localFile)
        {
            using var fs = File.OpenRead(localFile);
            Upload(remoteFile, fs);
        }
        /// <summary>
        /// 上传文件
        /// </summary>
        /// <param name="remoteFile"></param>
        /// <param name="stream"></param>
        public void Upload(string remoteFile, Stream stream)
        {
            CheckDisposed();
            CheckConnected();

            remoteFile = FormatPath(workingDirectory, remoteFile);
            var status = store!.CreateFile(out var fileHandle, out _, remoteFile, AccessMask.GENERIC_WRITE | AccessMask.SYNCHRONIZE,
                FileAttributes.Normal, ShareAccess.Write, CreateDisposition.FILE_CREATE, CreateOptions.FILE_NON_DIRECTORY_FILE, null);
            CheckStatus(status, nameof(store.CreateFile));

            int length = (int)client!.MaxWriteSize;
            int writeOffset = 0;
            while (true)
            {
                byte[] buffer = new byte[length];
                int count = stream.Read(buffer, 0, length);
                if (count == 0)
                {
                    break;
                }
                if (count < length)
                {
                    Array.Resize(ref buffer, count);
                }
                store.WriteFile(out _, fileHandle, writeOffset, buffer);
                CheckStatus(status, nameof(store.WriteFile));
                writeOffset += count;
            }

            status = store.CloseFile(fileHandle);
            CheckStatus(status, nameof(store.CloseFile));
        }
        /// <summary>
        /// 下载文件
        /// </summary>
        /// <param name="remoteFile"></param>
        /// <param name="localFile"></param>
        public void Download(string remoteFile, string localFile)
        {
            using var fs = File.OpenWrite(localFile);
            Download(remoteFile, fs);
        }
        /// <summary>
        /// 下载文件
        /// </summary>
        /// <param name="remoteFile"></param>
        /// <param name="stream"></param>
        public void Download(string remoteFile, Stream stream)
        {
            CheckDisposed();
            CheckConnected();

            remoteFile = FormatPath(workingDirectory, remoteFile);
            var status = store!.CreateFile(out var fileHandle, out _, remoteFile, AccessMask.GENERIC_READ | AccessMask.SYNCHRONIZE,
                FileAttributes.Normal, ShareAccess.Read, CreateDisposition.FILE_OPEN, CreateOptions.FILE_NON_DIRECTORY_FILE, null);
            CheckStatus(status, nameof(store.CreateFile));

            var length = (int)client!.MaxReadSize;
            long bytesRead = 0;
            while (true)
            {
                status = store.ReadFile(out var data, fileHandle, bytesRead, length);
                if (status == NTStatus.STATUS_END_OF_FILE)
                {
                    break;
                }
                CheckStatus(status, nameof(store.ReadFile));

                if (data.Length == 0)
                {
                    break;
                }
                bytesRead += data.Length;
                stream.Write(data, 0, data.Length);
            }

            if (stream.CanSeek)
            {
                stream.Seek(0, SeekOrigin.Begin);
            }

            status = store.CloseFile(fileHandle);
            CheckStatus(status, nameof(store.CloseFile));
        }

        public void Dispose()
        {
            disposed = true;
            if (store != null)
            {
                store.Disconnect();
            }
            if (client != null)
            {
                if (!string.IsNullOrEmpty(User))
                {
                    client.Logoff();
                }
                client.Disconnect();
            }
        }

        private void CheckDisposed()
        {
            if (disposed)
            {
                throw new ObjectDisposedException(nameof(SmbClient));
            }
        }
        private void CheckConnected()
        {
            if (client == null)
            {
                throw new InvalidOperationException("Connection needs to be established first");
            }
            if (!IsConnected)
            {
                throw new Exception($"Connection lost");
            }
        }
        private void CheckStatus(NTStatus status, string method, params string[] args)
        {
            if (status != NTStatus.STATUS_SUCCESS)
            {
                throw new Exception($"Error Occurs:{method}-{status}" + (args.Length > 0 ? string.Join("", args.Select(f => $"[{f}]")) : ""));
            }
        }
        private void GenerateClient()
        {
            CheckDisposed();
            if (client != null)
            {
                throw new Exception("client is exists");
            }

            if (!IPAddress.TryParse(Host, out var serverAddress))
            {
                IPAddress[] hostAddresses = Dns.GetHostAddresses(Host);
                if (hostAddresses.Length == 0)
                {
                    throw new Exception($"Cannot resolve host name {Host} to an IP address");
                }

                serverAddress = IPAddressHelper.SelectAddressPreferIPv4(hostAddresses);
            }

            var tranportType = NetBiosOverTCP ? SMBTransportType.NetBiosOverTCP : SMBTransportType.DirectTCPTransport;
            client = new SMB2Client();
            var connect = typeof(SMB2Client).GetMethod(nameof(client.Connect), BindingFlags.NonPublic | BindingFlags.Instance);
            connect?.Invoke(client, new object[] { serverAddress, tranportType, Port, 5000 });

            if (!IsConnected)
            {
                throw new Exception($"Cannot resolve host name {Host} to an IP address");
            }
            NTStatus status;
            if (!string.IsNullOrEmpty(User))
            {
                status = client.Login(Domain, User, Password);
                CheckStatus(status, nameof(client.Login));
            }
            var shares = client.ListShares(out status);
            store = client.TreeConnect(ShareName, out status);
            CheckStatus(status, nameof(client.TreeConnect));
        }
        private bool IsAbsolute(string path)
        {
            return path.StartsWith(@"\") || path.StartsWith(@"/");
        }
        private string FormatPath(string workingDirectory, string path)
        {
            List<string> paths = new List<string>();
            paths.AddRange(path.Split(new string[] { "/", "\\" }, StringSplitOptions.RemoveEmptyEntries));

            //如果是绝对路径,那么直接使用
            if (!IsAbsolute(path))
            {
                paths.Insert(0, workingDirectory);
            }
            return string.Join(seperator, paths.Where(f => !string.IsNullOrEmpty(f)));
        }
        private SmbClientFile[] GetList(string path, string pattern, bool? directory)
        {
            var status = store!.CreateFile(out var directoryHandle, out var _, path, AccessMask.GENERIC_READ, FileAttributes.Directory,
                ShareAccess.Read | ShareAccess.Write, CreateDisposition.FILE_OPEN_IF, CreateOptions.FILE_DIRECTORY_FILE, null);
            CheckStatus(status, nameof(store.CreateFile), path);

            List<SmbClientFile> files = new List<SmbClientFile>();
            if (store is SMB1FileStore fileStore)
            {
                pattern = $"{path}{seperator}{pattern}";
                status = ((SMB1FileStore)store).QueryDirectory(out var list, pattern, FindInformationLevel.SMB_FIND_FILE_DIRECTORY_INFO);
                CheckStatus(status, nameof(store.QueryDirectory), pattern);

                Action<FindFileDirectoryInfo> action = directory switch
                {
                    false => fileDirectoryInfo =>
                    {
                        if (!fileDirectoryInfo.ExtFileAttributes.HasFlag(ExtendedFileAttributes.Directory))
                        {
                            var modified = fileDirectoryInfo.LastWriteTime ?? fileDirectoryInfo.CreationTime;
                            files.Add(new SmbClientFile(fileDirectoryInfo.FileName, false, modified, fileDirectoryInfo.CreationTime));
                        }
                    }
                    ,
                    true => fileDirectoryInfo =>
                    {
                        if (fileDirectoryInfo.ExtFileAttributes.HasFlag(ExtendedFileAttributes.Directory))
                        {
                            var modified = fileDirectoryInfo.LastWriteTime ?? fileDirectoryInfo.CreationTime;
                            files.Add(new SmbClientFile(fileDirectoryInfo.FileName, true, modified, fileDirectoryInfo.CreationTime));
                        }
                    }
                    ,
                    _ => fileDirectoryInfo =>
                    {
                        var isDirectory = fileDirectoryInfo.ExtFileAttributes.HasFlag(ExtendedFileAttributes.Directory);
                        var modified = fileDirectoryInfo.LastWriteTime ?? fileDirectoryInfo.CreationTime;
                        files.Add(new SmbClientFile(fileDirectoryInfo.FileName, isDirectory, modified, fileDirectoryInfo.CreationTime));
                    }
                };

                foreach (var l in list)
                {
                    if (l is FindFileDirectoryInfo fileDirectoryInfo)
                    {
                        if (fileDirectoryInfo.FileName == "." || fileDirectoryInfo.FileName == "..")
                        {
                            continue;
                        }
                        action(fileDirectoryInfo);
                    }
                }
            }
            else
            {
                status = store.QueryDirectory(out var list, directoryHandle, pattern, FileInformationClass.FileDirectoryInformation);
                if (status != NTStatus.STATUS_NO_MORE_FILES)
                {
                    CheckStatus(status, nameof(store.QueryDirectory), pattern);
                }

                Action<FileDirectoryInformation> action = directory switch
                {
                    false => file =>
                    {
                        if (!file.FileAttributes.HasFlag(FileAttributes.Directory))
                        {
                            files.Add(new SmbClientFile(file.FileName, false, file.ChangeTime, file.CreationTime));
                        }
                    }
                    ,
                    true => file =>
                    {
                        if (file.FileAttributes.HasFlag(FileAttributes.Directory))
                        {
                            files.Add(new SmbClientFile(file.FileName, true, file.ChangeTime, file.CreationTime));
                        }
                    }
                    ,
                    _ => file =>
                    {
                        files.Add(new SmbClientFile(file.FileName, file.FileAttributes.HasFlag(FileAttributes.Directory), file.ChangeTime, file.CreationTime));
                    }
                };

                foreach (var l in list)
                {
                    if (l is FileDirectoryInformation file)
                    {
                        if (file.FileName == "." || file.FileName == "..")
                        {
                            continue;
                        }
                        action(file);
                    }
                }
            }

            status = store.CloseFile(directoryHandle);
            CheckStatus(status, nameof(store.CloseFile));

            return files.ToArray();
        }
    }
    public class SmbClientFile
    {
        public SmbClientFile(string name, bool isDirectory, DateTime? lastModifiedTime, DateTime? creationTime)
        {
            Name = name;
            IsDirectory = isDirectory;
            if (lastModifiedTime != null)
            {
                if (lastModifiedTime.Value.Kind == DateTimeKind.Utc)
                {
                    lastModifiedTime = lastModifiedTime.Value.ToLocalTime();
                }
            }
            LastModifiedTime = lastModifiedTime;

            if (creationTime != null)
            {
                if (creationTime.Value.Kind == DateTimeKind.Utc)
                {
                    creationTime = creationTime.Value.ToLocalTime();
                }
            }
            CreationTime = creationTime;
        }

        /// <summary>
        /// 名称
        /// </summary>
        public string Name { get; }
        /// <summary>
        /// 是否是文件
        /// </summary>
        public bool IsDirectory { get; }
        /// <summary>
        /// 最后修改时间
        /// </summary>
        public DateTime? LastModifiedTime { get; }
        /// <summary>
        /// 创建时间
        /// </summary>
        public DateTime? CreationTime { get; }
    }

  测试代码:    

    public void Main(string[] args)
    {
        var value = "hello Smb";
        using var ms = new MemoryStream(Encoding.UTF8.GetBytes(value));

        using SmbClient client = new SmbClient("localhost", "test")
        {
            User = "admin",
            Domain = "",
            Password = "123456",
            NetBiosOverTCP = false,
            Port = 445
            //NetBiosOverTCP = true,
            //Port = 139
        };

        //开始连接
        client.Connect();

        //设置工作目录
        if (!client.DirectoryIsExist("RootPath"))
        {
            client.CreateDirectory("RootPath", true);
        }
        client.SetWorkingDirectory("RootPath");

        string filePath = "test&a#1";
        string fileName = Path.Combine(filePath, "test$a#2.txt");

        //目录是否存在
        var result = client.DirectoryIsExist(filePath);
        if (!result)
        {
            //创建目录
            client.CreateDirectory(filePath, true);
        }

        //文件是否存在
        result = client.FileIsExist(fileName);
        if (result)
        {
            //删除文件
            client.Delete(fileName);
        }

        //上传文件
        client.Upload(fileName, ms);

        //获取指定目录下的文件
        var files = client.GetFiles(filePath);
        //获取指定目录下的子目录
        var directories = client.GetDirectories("");
        //获取指定目录下的文件及子目录
        var list = client.GetList(filePath);

        //下载文件
        using var fs = new MemoryStream();
        client.Download(fileName, fs);

        //删除目录及它下面的所有文件
        client.RemoveDirectory(filePath);
    }

  这个辅助类支持两种协议,可以选择使用(一般都是DTP协议):  

    1、NetBIOS over TCP/IP(NBT),默认端口139
    2、Direct TCP Transport(DTP),默认端口445

  

  结语

  这两个辅助类是个人在.Net Core开发过程中封装使用的,所以分享一下,希望对有需要的朋友有帮助。

 

posted @ 2024-01-06 12:47  没有星星的夏季  阅读(420)  评论(0编辑  收藏  举报