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开发过程中封装使用的,所以分享一下,希望对有需要的朋友有帮助。
一个专注于.NetCore的技术小白