diff --git a/src/FileProviderExtensions.cs b/src/FileProviderExtensions.cs
index c69abcb..04dbb4e 100644
--- a/src/FileProviderExtensions.cs
+++ b/src/FileProviderExtensions.cs
@@ -1,4 +1,5 @@
using System.IO;
+using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.FileProviders;
@@ -26,7 +27,8 @@ public static byte[] ReadAsBytes(this IFileInfo file)
/// Reads the entire file into a byte array.
/// The file to read.
/// (awaitable) The contents of the file as a byte array.
- public static async Task ReadAsBytesAsync(this IFileInfo file)
+ /// Cancellation token.
+ public static async Task ReadAsBytesAsync(this IFileInfo file, CancellationToken cancel = default)
{
using var stream = file.CreateReadStream();
if (stream is MemoryStream ms)
@@ -35,7 +37,7 @@ public static async Task ReadAsBytesAsync(this IFileInfo file)
}
using var output = new MemoryStream();
- await stream.CopyToAsync(output);
+ await stream.CopyToAsync(output, 81920, cancel);
return output.ToArray();
}
diff --git a/src/InMemory/InMemoryFileInfo.cs b/src/InMemory/InMemoryFileInfo.cs
index 8b3fdec..43aac05 100644
--- a/src/InMemory/InMemoryFileInfo.cs
+++ b/src/InMemory/InMemoryFileInfo.cs
@@ -33,7 +33,7 @@ public InMemoryFileInfo(string name, string content, DateTimeOffset? lastModifie
///
public string? Name { get; }
///
- public DateTimeOffset LastModified { get; }
+ public DateTimeOffset LastModified { get; internal set; }
///
public bool Exists => true;
diff --git a/src/InMemory/InMemoryFileProvider.cs b/src/InMemory/InMemoryFileProvider.cs
index deb2685..d856f6e 100644
--- a/src/InMemory/InMemoryFileProvider.cs
+++ b/src/InMemory/InMemoryFileProvider.cs
@@ -200,4 +200,22 @@ private static string NormalizePath(string path)
if (path == null) { throw new ArgumentNullException(nameof(path)); }
return path.Replace("\\", "/").TrimStart('/');
}
+
+ ///
+ public void ChangeLastModified(string path, DateTimeOffset lastModified)
+ {
+ var fileInfo = this[path];
+ if (fileInfo is null)
+ {
+ throw new FileNotFoundException("File not found", path);
+ }
+
+ if (fileInfo is InMemoryFileInfo inMemoryFileInfo)
+ {
+ inMemoryFileInfo.LastModified = lastModified;
+ return;
+ }
+
+ throw new InvalidOperationException("Cannot change last modified on a non-in-memory file");
+ }
}
\ No newline at end of file
diff --git a/src/Mirality.FileProviders.InMemory.csproj b/src/Mirality.FileProviders.InMemory.csproj
index 95e89c3..d4205b8 100644
--- a/src/Mirality.FileProviders.InMemory.csproj
+++ b/src/Mirality.FileProviders.InMemory.csproj
@@ -1,7 +1,7 @@
- netstandard2.0;netstandard2.1
+ netstandard2.0;netstandard2.1;net8.0;net9.0;net10.0
10
6.2.1
enable
diff --git a/src/WritableFileProvider.cs b/src/WritableFileProvider.cs
index 4c3d2bb..9372dbc 100644
--- a/src/WritableFileProvider.cs
+++ b/src/WritableFileProvider.cs
@@ -1,9 +1,9 @@
-using Microsoft.Extensions.FileProviders;
-using System;
+using System;
using System.IO;
using System.Text;
-using System.Threading.Tasks;
using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.FileProviders;
namespace Mirality.FileProviders;
@@ -19,6 +19,13 @@ public interface IWritableFileProvider : IFileProvider
/// It's not an error if the file already does not exist.
/// The full relative file path.
void Delete(string path);
+
+ ///
+ /// Changes the last modified time of a file.
+ ///
+ /// The full relative file path.
+ /// When the file was last modified.
+ void ChangeLastModified(string path, DateTimeOffset lastModified);
}
/// An that provides synchronous write operations.
diff --git a/src/WritablePhysicalFileProvider.cs b/src/WritablePhysicalFileProvider.cs
index 535bb3b..d1d5a77 100644
--- a/src/WritablePhysicalFileProvider.cs
+++ b/src/WritablePhysicalFileProvider.cs
@@ -1,10 +1,10 @@
-using Microsoft.Extensions.FileProviders;
-using Microsoft.Extensions.Primitives;
-using System;
+using System;
using System.IO;
using System.Linq;
-using System.Threading.Tasks;
using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.FileProviders;
+using Microsoft.Extensions.Primitives;
namespace Mirality.FileProviders;
@@ -138,4 +138,11 @@ public void Delete(string path)
file.Delete();
}
+
+ ///
+ public void ChangeLastModified(string path, DateTimeOffset lastModified)
+ {
+ var file = GetPhysicalFileInfo(path);
+ file.LastWriteTimeUtc = lastModified.UtcDateTime;
+ }
}
\ No newline at end of file
diff --git a/tests/InMemoryFileProviderTests.cs b/tests/InMemoryFileProviderTests.cs
index edc1843..0650423 100644
--- a/tests/InMemoryFileProviderTests.cs
+++ b/tests/InMemoryFileProviderTests.cs
@@ -93,6 +93,21 @@ public void ExistingFiles()
});
}
+ [Test]
+ public void ChangeLastModified()
+ {
+ var provider = new InMemoryFileProvider();
+ var initialTimestamp = new DateTimeOffset(2021, 12, 25, 12, 34, 56, TimeSpan.Zero);
+ var updatedTimestamp = new DateTimeOffset(2022, 1, 2, 3, 4, 5, TimeSpan.Zero);
+
+ provider.Write("test.txt", "hello world", initialTimestamp);
+ provider.ChangeLastModified("test.txt", updatedTimestamp);
+
+ var file = provider.GetFileInfo("test.txt");
+ Assert.That(file.Exists);
+ Assert.That(file.LastModified, Is.EqualTo(updatedTimestamp));
+ }
+
[Test]
public void FileInfoForDirectories()
{
diff --git a/tests/Mirality.FileProviders.InMemory.Tests.csproj b/tests/Mirality.FileProviders.InMemory.Tests.csproj
index 593812e..988e921 100644
--- a/tests/Mirality.FileProviders.InMemory.Tests.csproj
+++ b/tests/Mirality.FileProviders.InMemory.Tests.csproj
@@ -1,7 +1,7 @@
- net48;net6.0
+ net48;net8.0;net9.0;net10.0
10.0
enable
enable
diff --git a/tests/WritablePhysicalFileProviderTests.cs b/tests/WritablePhysicalFileProviderTests.cs
index 613b2ad..6066446 100644
--- a/tests/WritablePhysicalFileProviderTests.cs
+++ b/tests/WritablePhysicalFileProviderTests.cs
@@ -163,7 +163,7 @@ public async Task WriteBytesAsyncGeneric()
var timestamp = new DateTimeOffset(2021, 12, 25, 12, 34, 56, TimeSpan.Zero);
var bytes = new byte[] { 1, 2, 3, 4, 5, 6 };
- var file = await ((IWritableFileProvider) provider).WriteAsync("test.bin", bytes, timestamp);
+ var file = await ((IWritableFileProvider)provider).WriteAsync("test.bin", bytes, timestamp);
Assert.That(file.Exists);
Assert.That(_PhysicalFileProvider.GetFileInfo("test.bin").Exists);
@@ -176,13 +176,28 @@ public async Task WriteTextAsyncGeneric()
var provider = new WritablePhysicalFileProvider(_TestDirectory, _PhysicalFileProvider);
var timestamp = new DateTimeOffset(2021, 12, 25, 12, 34, 56, TimeSpan.Zero);
- var file = await ((IWritableFileProvider) provider).WriteAsync("test.txt", "hello world", timestamp);
+ var file = await ((IWritableFileProvider)provider).WriteAsync("test.txt", "hello world", timestamp);
Assert.That(file.Exists);
Assert.That(_PhysicalFileProvider.GetFileInfo("test.txt").Exists);
Assert.That(_PhysicalFileProvider.GetFileInfo("test.txt").LastModified, Is.EqualTo(timestamp));
}
+ [Test]
+ public async Task ChangeLastModified()
+ {
+ var provider = new WritablePhysicalFileProvider(_TestDirectory, _PhysicalFileProvider);
+ var initialTimestamp = new DateTimeOffset(2021, 12, 25, 12, 34, 56, TimeSpan.Zero);
+ var updatedTimestamp = new DateTimeOffset(2022, 1, 2, 3, 4, 5, TimeSpan.Zero);
+
+ await provider.WriteAsync("test.txt", "hello world", initialTimestamp);
+ provider.ChangeLastModified("test.txt", updatedTimestamp);
+
+ var file = _PhysicalFileProvider.GetFileInfo("test.txt");
+ Assert.That(file.Exists);
+ Assert.That(file.LastModified, Is.InRange(updatedTimestamp.AddSeconds(-1), updatedTimestamp.AddSeconds(1))); // for some reason this is one second off.
+ }
+
[Test]
public void Delete()
{