![]() |
VOOZH | about |
dotnet add package WebDAVClient --version 2.7.0
NuGet\Install-Package WebDAVClient -Version 2.7.0
<PackageReference Include="WebDAVClient" Version="2.7.0" />
<PackageVersion Include="WebDAVClient" Version="2.7.0" />Directory.Packages.props
<PackageReference Include="WebDAVClient" />Project file
paket add WebDAVClient --version 2.7.0
#r "nuget: WebDAVClient, 2.7.0"
#:package WebDAVClient@2.7.0
#addin nuget:?package=WebDAVClient&version=2.7.0Install as a Cake Addin
#tool nuget:?package=WebDAVClient&version=2.7.0Install as a Cake Tool
WebDAV Client for .Net Core, strongly typed, async and open-sourced, implemented in C#.
WebDAVClient is based originally on https://github.com/kvdb/WebDAVClient. I've added Async support (instead of Callback), as well strong-types responses.
WebDAVClient is available as a NuGet package
ICredentials), and Bearer / OAuth 2.0 (via static token or async refreshable provider)<allprop/>, targeted <prop>, and <propname/> PROPFIND variants)Overwrite header and optional destination lock-token)LockInfoIf header on PUT / DELETE / MOVE / COPYLockFile / LockFolder / UnlockFile / UnlockFolder / RefreshLock methods returning a strongly-typed LockInfo.SetProperty / RemoveProperty methods for managing custom (dead) properties.Overwrite header on COPY / MOVE (RFC 4918 §9.8.3 / §9.9.3): always sent (default T); new optional overwrite parameter to opt out. 204 No Content accepted as success.Depth: infinity on DELETE (RFC 4918 §9.6.1): now sent as required by the spec for collection deletes.If lock-token submission on PUT / DELETE / MOVE / COPY (RFC 4918 §10.4): new optional lockToken (and source / destination variants) parameters so locked-resource servers no longer reject modifications with 423 Locked.Client.cs into focused, unit-testable helper classes under WebDAVClient.Helpers. No public-API change.<prop> set or discover property names via <propname/>, in addition to the existing <allprop/> behaviour.OPTIONS support (RFC 4918 §9.1, RFC 9110 §9.3.7): new GetServerOptions method returning a strongly-typed ServerOptions (DAV compliance classes + allowed methods).Client constructor overloads — static token or async refreshable provider — backed by a public WebDAVClient.Authentication.BearerTokenAuthenticationHandler.ResponseParser now sets DtdProcessing = Prohibit and XmlResolver = null explicitly.ServerCertificateValidationCallback is now actually plumbed into the underlying HttpClientHandler.BuildServerUrl validates that any absolute URI it accepts belongs to the configured Server host.CustomHeaders entries are validated for CR/LF in both name and value before being sent.List() no longer issues an await GetServerUrl per returned item — the encoded base path is resolved once and reused, dramatically reducing async overhead on large listings.CustomHeaders and the internal headers dictionary are no longer copied or double-looked-up per request; they are iterated in place.MOVE/COPY request bodies and PROPFIND body to eliminate per-call allocations.ResponseParser avoids a per-node string allocation when reading element local names.List() now uses OrdinalIgnoreCase instead of CurrentCultureIgnoreCase — avoids the Turkish dotted/dotless I issue and the slower culture-aware path.uploadTimeout is set, the upload HttpClient no longer disposes the HttpClientHandler it shares with the main client.WebDAVClient.UnitTests project covering the public surface.IDisposable to avoid HttpClient leakCancellationTokenClient implements IDisposable — wrap it in a using to make sure the underlying HttpClient/HttpClientHandler are released. Most async methods accept an optional CancellationToken so requests can be cancelled cleanly.
// Pick one of the constructors below.
// (1) Basic authentication
using IClient client = new Client(
new NetworkCredential { UserName = "USERNAME", Password = "PASSWORD" });
// (2) ...or no authentication
// using IClient client = new Client(new NetworkCredential());
// (3) ...or supply your own HttpClient (e.g. for IHttpClientFactory / DI scenarios)
// using IClient client = new Client(myHttpClient);
// (4) ...or Bearer / OAuth 2.0 — see "What's new in 2.7.0" below for refresh-aware variant
// using IClient client = new Client("eyJ0eXAiOiJKV1Qi...");
// Set basic information for the WebDAV provider
client.Server = "https://dav.example.com/";
client.BasePath = "/dav/";
// Optional configuration
client.Port = 8443; // override the default port
client.UserAgent = "MyApp"; // override the default User-Agent
client.CustomHeaders = new[] // sent with every request
{
new KeyValuePair<string, string>("X-Tenant", "acme"),
};
// Most operations accept a CancellationToken
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
var token = cts.Token;
// List items in the root folder
var files = await client.List(cancellationToken: token);
// Find folder named 'Test'
var folder = files.FirstOrDefault(f => f.Href.EndsWith("/Test/"));
// Reload folder 'Test'
var folderReloaded = await client.GetFolder(folder.Href, cancellationToken: token);
// Retrieve list of items in 'Test' folder
var folderFiles = await client.List(folderReloaded.Href, cancellationToken: token);
// Find first file in 'Test' folder
var folderFile = folderFiles.FirstOrDefault(f => f.IsCollection == false);
var tempFileName = Path.GetTempFileName();
// Download item into a temporary file
using (var tempFile = File.OpenWrite(tempFileName))
using (var stream = await client.Download(folderFile.Href, cancellationToken: token))
await stream.CopyToAsync(tempFile, token);
// Upload file back to webdav
var tempName = Path.GetRandomFileName();
using (var fileStream = File.OpenRead(tempFileName))
{
var fileUploaded = await client.Upload(folder.Href, fileStream, tempName, cancellationToken: token);
}
// Create a folder
var tempFolderName = Path.GetRandomFileName();
var isfolderCreated = await client.CreateDir("/", tempFolderName, cancellationToken: token);
// Copy a file
await client.CopyFile(folderFile.Href, "/" + tempFolderName + "/copy.bin", cancellationToken: token);
// Copy a folder
await client.CopyFolder(folder.Href, "/" + tempFolderName + "-copy/", cancellationToken: token);
// Delete created folder
var folderCreated = await client.GetFolder("/" + tempFolderName, cancellationToken: token);
await client.DeleteFolder(folderCreated.Href, cancellationToken: token);
// Static token (Nextcloud app-password, long-lived service token, …)
using IClient client = new Client("eyJ0eXAiOiJKV1Qi...");
// Async, refreshable token provider (Azure AD / MSAL / IdentityModel / custom)
using IClient client = new Client(async ct => await tokenSource.GetTokenAsync(ct));
Returning null / empty from the provider omits the Authorization header (the server then naturally returns 401). The handler is also exposed publicly as WebDAVClient.Authentication.BearerTokenAuthenticationHandler if you need to compose it into your own HttpClient pipeline.
var options = await client.GetServerOptions("/dav/", cancellationToken: token);
if (!options.IsWebDavServer)
throw new InvalidOperationException("Endpoint is not a WebDAV server");
bool supportsLock = options.IsClass2 && options.SupportsMethod("LOCK");
// Take an exclusive write lock on a file (default timeout: 600 seconds)
var info = await client.LockFile("/dav/report.docx", owner: "alice", cancellationToken: token);
// Use the lock token on subsequent writes via the If header
using (var fs = File.OpenRead(localPath))
await client.Upload("/dav/", fs, "report.docx", lockToken: info.Token, cancellationToken: token);
// Extend / release the lock
await client.RefreshLock("/dav/report.docx", info.Token, timeoutSeconds: 600, cancellationToken: token);
await client.UnlockFile("/dav/report.docx", info.Token, cancellationToken: token);
LockInfo.Token is accepted in either bare (opaquelocktoken:abc) or <opaquelocktoken:abc> form everywhere it's used.
// Set a custom dead property in your own namespace (the DAV: namespace is reserved for live properties and is rejected client-side)
await client.SetProperty("/dav/report.docx", "author", "https://example.com/ns", "Alice", token);
// Remove it later
await client.RemoveProperty("/dav/report.docx", "author", "https://example.com/ns", token);
<prop> and <propname/>// Ask only for the properties you need (saves bandwidth on large directories)
var props = new[]
{
new PropertyName("getetag", "DAV:"),
new PropertyName("getcontentlength", "DAV:"),
new PropertyName("author", "https://example.com/ns"),
};
var items = await client.List("/dav/", depth: 1, properties: props, cancellationToken: token);
foreach (var item in items)
{
// Standard DAV: live properties light up on Item directly (Etag, ContentLength, …)
// Custom properties land in FoundProperties / NotFoundProperties.
var author = item.FoundProperties?.FirstOrDefault(p => p.Name.LocalName == "author");
}
// Discover what properties a resource exposes
var names = await client.GetFilePropertyNames("/dav/report.docx", cancellationToken: token);
foreach (var n in names.AvailablePropertyNames)
Console.WriteLine($"{n.Namespace}:{n.LocalName}");
// PUT / DELETE / MOVE / COPY now accept the relevant lock tokens, so locked-resource servers stop rejecting the request with 423 Locked.
await client.DeleteFile("/dav/report.docx", lockToken: info.Token, cancellationToken: token);
await client.MoveFile("/dav/old.txt", "/dav/new.txt",
sourceLockToken: srcToken, destinationLockToken: dstToken, cancellationToken: token);
// Opt out of clobbering an existing destination (sends Overwrite: F → server returns 412 Precondition Failed)
await client.CopyFile("/dav/a.txt", "/dav/b.txt", overwrite: false, cancellationToken: token);
You can contact me on twitter @saguiitay or on my website
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 net8.0 is compatible. net8.0-android net8.0-android was computed. net8.0-browser net8.0-browser was computed. net8.0-ios net8.0-ios was computed. net8.0-maccatalyst net8.0-maccatalyst was computed. net8.0-macos net8.0-macos was computed. net8.0-tvos net8.0-tvos was computed. net8.0-windows net8.0-windows was computed. net9.0 net9.0 was computed. net9.0-android net9.0-android was computed. net9.0-browser net9.0-browser was computed. net9.0-ios net9.0-ios was computed. net9.0-maccatalyst net9.0-maccatalyst was computed. net9.0-macos net9.0-macos was computed. net9.0-tvos net9.0-tvos was computed. net9.0-windows net9.0-windows was computed. net10.0 net10.0 is compatible. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
Showing the top 3 NuGet packages that depend on WebDAVClient:
| Package | Downloads |
|---|---|
|
SuperiorAcumaticaPackage
Dependencies required to compile the SuperiorAcumaticaSolution for Acumatica 2026 R1 Build 26.100.0175 |
|
|
IServNET
C# Api for interacting with the IServ System. |
|
|
YngveHestem.Storage.WebDAV
WebDAV storage provider implementation for the YngveHestem.Storage abstractions. |
Showing the top 1 popular GitHub repositories that depend on WebDAVClient:
| Repository | Stars |
|---|---|
|
EasyTidy/EasyTidy
EasyTidy A simple file auto-classification tool makes it easy to create automatic workflows with files. / EasyTidy 一个简单的文件自动分类整理工具 轻松创建文件的自动工作流程
|
| Version | Downloads | Last Updated |
|---|---|---|
| 2.7.0 | 421 | 5/1/2026 |
| 2.5.5 | 108 | 5/1/2026 |
| 2.5.4 | 101 | 5/1/2026 |
| 2.5.3 | 101 | 5/1/2026 |
| 2.5.2 | 97 | 5/1/2026 |
| 2.5.1 | 100 | 5/1/2026 |
| 2.5.0 | 109 | 5/1/2026 |
| 2.4.0 | 100 | 5/1/2026 |
| 2.3.0 | 6,076 | 9/4/2025 |
| 2.2.1 | 46,116 | 8/25/2023 |
| 2.2.0 | 341 | 8/25/2023 |
| 2.1.0 | 74,728 | 8/25/2021 |
| 2.0.0 | 5,842 | 11/19/2020 |
| 1.1.4 | 8,707 | 6/20/2018 |
| 1.1.3 | 5,054 | 11/18/2017 |
| 1.1.2 | 124,952 | 8/8/2016 |
| 1.1.1 | 47,835 | 12/27/2015 |
| 1.1.0 | 1,579 | 12/13/2015 |
| 1.0.23 | 1,667 | 12/10/2015 |
| 1.0.21 | 9,446 | 9/2/2015 |
* Feature: WebDAV LOCK / UNLOCK support (RFC 4918 §9.10–9.11).
* Feature: WebDAV PROPPATCH support — set / remove custom properties (RFC 4918 §9.2).
* Bug fix: COPY / MOVE now send the Overwrite header (RFC 4918 §9.8.3 / §9.9.3); 204 No Content accepted as success.
* Bug fix: DELETE now sends Depth: infinity (RFC 4918 §9.6.1).
* Feature: per-call lock-token submission via the If header on PUT / DELETE / MOVE / COPY (RFC 4918 §10.4).
* Internal refactor: extracted Client.cs static helpers into focused, unit-testable helper classes under WebDAVClient.Helpers.
* Feature: PROPFIND request-body variants — <prop> and <propname/> in addition to <allprop/> (RFC 4918 §9.1).
* Feature: HTTP OPTIONS support — discover DAV compliance classes and allowed methods (RFC 4918 §9.1, RFC 9110 §9.3.7).
* Feature: Bearer token / OAuth 2.0 authentication via new Client constructor overloads (static token or async refreshable provider).