WriteableFileProviders.Physical 2027.1.1

dotnet add package WriteableFileProviders.Physical --version 2027.1.1
                    
NuGet\Install-Package WriteableFileProviders.Physical -Version 2027.1.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="WriteableFileProviders.Physical" Version="2027.1.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="WriteableFileProviders.Physical" Version="2027.1.1" />
                    
Directory.Packages.props
<PackageReference Include="WriteableFileProviders.Physical" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add WriteableFileProviders.Physical --version 2027.1.1
                    
#r "nuget: WriteableFileProviders.Physical, 2027.1.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package WriteableFileProviders.Physical@2027.1.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=WriteableFileProviders.Physical&version=2027.1.1
                    
Install as a Cake Addin
#tool nuget:?package=WriteableFileProviders.Physical&version=2027.1.1
                    
Install as a Cake Tool

WriteableFileProviders.Physical

A folder on the local disk as an IWriteableFileProvider, with atomic writes and change notifications. Part of WriteableFileProviders.

Install

dotnet add package WriteableFileProviders.Physical

Needs .NET 10, and is native AOT compatible. Keep every WriteableFileProviders package in an app on the same major version.

Use

using WriteableFileProviders;
using WriteableFileProviders.Physical;

await using var files = new WriteablePhysicalFileProvider("/srv/files");

await files.UpdateFileAsync("reports/2026.csv", stream);                // creates reports/ if missing
await files.UpdateFileAsync("app.log", line, UpdateFileOptions.Append);
await using var slice = await files.ReadRangeAsync("big.bin", 1_000_000, 4096);
var changed = files.Watch("reports/*.csv");

From configuration, with WriteableFileProviderSerializer.FromConfiguration(section) or services.AddWriteableFileProvider(section):

{ "Files": { "Type": "WriteablePhysicalFileProvider", "RootDirectoryPath": "/srv/files" } }

Configuration

Option Default Meaning
RootDirectoryPath required The folder the provider is confined to: absolute (/srv/files, C:\files, \\server\share\files) or relative to the current directory, resolved once when the provider is built. It need not exist; the first write creates it.
RootDirectorySegments The same folder as segments, the form 2026 stored: ["root:", "srv", "files"], ["C:", "files"], ["\\", "server", "share"]. Set this or RootDirectoryPath.
RefuseLinks false Refuse a path that names or goes through a symbolic link, junction or Windows mount point inside the root (ArgumentException), and leave links out of listings. The root itself may be a link. New in 2027.1.0: an older version ignores it and follows links.

Authentication

None. The provider reads and writes with the permissions of the process.

What's different

  • Writes are atomic. An overwrite, an append, a copy or a local stream's commit writes .wfp-<guid>.tmp next to the target, then renames it over the target. Listings hide these files; a crash can leave one. DeleteTempFilesAsync(olderThan) deletes those older than an age you choose, which must be longer than your longest write, plus any lag of a file server's clock: a write whose temp file goes fails, and its file keeps its content. It doesn't walk into linked folders, and skips a folder below the root that it may not list; a root it can't read throws.
  • An append copies the whole file, so frequent appends to a large file (a log) are expensive. It first copies your stream to a hidden .wfp-<guid>.tmp beside the file (on Linux and macOS readable by the process's user only), so that after a concurrent write it can read the file again and append after it, up to five attempts in all. On Linux and macOS, an append that loses a new name to another writer still throws FileAlreadyExistsException while the file there is empty (a move's destination, a file system without hard links, or a file another writer left empty): it can't tell such a file from one a rename is about to fill, which would undo the append. On Windows, which doesn't rename over a file that is open, one of two appends to one file at once can fail with UnauthorizedAccessException.
  • A write puts a new file at the path. Other hard links keep the old content, and a symbolic link at the path becomes a file. On Windows the file takes the folder's permissions; on Linux and macOS, the old file's mode bits. ACLs and extended attributes are not kept.
  • IfMatch is not atomic. It is checked just before the rename. ETags come from the last-write time and length.
  • On Windows, a write to a file that is open for reading fails (UnauthorizedAccessException or IOException), and the file keeps its content. Linux and macOS let the write go ahead.
  • Case is ignored on Windows and macOS, and matters elsewhere. : is refused in names everywhere. On Windows, so are names Windows can't hold or would read as another name: report.txt., a?b, CON, con.txt. Listings leave out such names, and on Linux and macOS, names holding : or \.
  • Symbolic links and junctions inside the root are followed, wherever they point, unless you set RefuseLinks. Without it, a recursive listing lists a linked folder but doesn't walk into it. RefuseLinks checks the path just before each operation, so a process that can create links inside the root can still swap one in between, except for a folder delete on Linux (x64, Arm, Arm64), macOS and Windows, which opens every folder from the root without following a link. Hard links and mounts on Linux and macOS aren't visible as links, and other reparse points (cloud placeholders, Windows container layers) aren't links. The polling Watch can still report a change behind a linked folder.
  • A folder delete never leaves the folder on Linux (x64, Arm, Arm64), macOS and Windows. It opens each entry in its folder without following a link and removes it there, so a link inside, even one swapped in while it runs, is removed as itself. It goes on past what it can't delete and throws the first failure; cancelled, it stops between entries, the folder partly deleted. On Windows a folder holding a junction is deleted. On macOS the folder that holds the deleted one, and with RefuseLinks every folder on the way, must be readable. Elsewhere (Linux on other architectures, other systems) the delete goes by path, as .NET's does, and a link swapped in while it runs can still steer it outside the folder.
  • No content types. UpdateFileOptions.ContentType is ignored; ContentType is null.
  • Watch works like ASP.NET Core's PhysicalFileProvider: a glob relative to the root. Hidden and dot-prefixed files aren't watched. Where file system events don't arrive (some network and container mounts), set DOTNET_USE_POLLING_FILE_WATCHER=1.
  • No native signed URLs. GetSignedUrlAsync throws NotSupportedException. For download and upload links, use the gateway in WriteableFileProviders.AspNetCore.

Learn more

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2027.1.1 43 10/13/2026
2027.1.1-rc.1 42 10/13/2026
2027.1.0 73 10/12/2026
2027.1.0-rc.1 49 10/12/2026
2027.0.0 360 10/11/2026
2027.0.0-rc.2 63 10/11/2026
Loading failed