diff --git a/Server/Controllers/DownloadController.cs b/Server/Controllers/DownloadController.cs
index c173f70ee..c0f7a2655 100644
--- a/Server/Controllers/DownloadController.cs
+++ b/Server/Controllers/DownloadController.cs
@@ -20,7 +20,7 @@ public IActionResult Download()
if (System.IO.File.Exists(file) == false)
return NotFound();
- return new FileStreamResult(System.IO.File.OpenRead(file), "application/octet-stream")
+ return new FileStreamResult(FileOpenHelper.OpenRead_NoLocks(file), "application/octet-stream")
{
FileDownloadName = zipName
};
diff --git a/Server/Controllers/PluginController.cs b/Server/Controllers/PluginController.cs
index 749e70066..83715094a 100644
--- a/Server/Controllers/PluginController.cs
+++ b/Server/Controllers/PluginController.cs
@@ -218,7 +218,7 @@ public FileStreamResult DownloadPackage([FromRoute] string package)
try
{
- return File(System.IO.File.OpenRead(file), "application/octet-stream");
+ return File(FileOpenHelper.OpenRead_NoLocks(file), "application/octet-stream");
}
catch(Exception ex)
{
diff --git a/Server/Helpers/Gzipper.cs b/Server/Helpers/Gzipper.cs
index 6d1aa54a7..8605d1a59 100644
--- a/Server/Helpers/Gzipper.cs
+++ b/Server/Helpers/Gzipper.cs
@@ -22,7 +22,7 @@ public static bool CompressFile(string inputFile, string outputFile, bool delete
if (File.Exists(outputFile))
File.Delete(outputFile);
- using (FileStream originalFileStream = File.Open(inputFile, FileMode.Open))
+ using (FileStream originalFileStream = FileOpenHelper.OpenRead_NoLocks(inputFile))
{
using FileStream compressedFileStream = File.Create(outputFile);
using var compressor = new GZipStream(compressedFileStream, CompressionMode.Compress);
@@ -55,7 +55,7 @@ public static bool DecompressFile(string inputFile, string outputFile, bool dele
if (File.Exists(outputFile))
File.Delete(outputFile);
- using FileStream compressedFileStream = File.Open(inputFile, FileMode.Open);
+ using FileStream compressedFileStream = FileOpenHelper.OpenRead_NoLocks(inputFile);
using FileStream outputFileStream = File.Create(outputFile);
using var decompressor = new GZipStream(compressedFileStream, CompressionMode.Decompress);
decompressor.CopyTo(outputFileStream);
@@ -96,7 +96,7 @@ public static void CompressToFile(string file, string contents)
/// the decompresses file contents
public static string DecompressFileToString(string inputFile, int lines = 0, int bytes = 0)
{
- using FileStream compressedFileStream = File.Open(inputFile, FileMode.Open);
+ using FileStream compressedFileStream = FileOpenHelper.OpenRead_NoLocks(inputFile);
using MemoryStream outputStream = new MemoryStream();
using var decompressor = new GZipStream(compressedFileStream, CompressionMode.Decompress);
diff --git a/Server/Server.csproj b/Server/Server.csproj
index a85aa98a5..c04cb1598 100644
Binary files a/Server/Server.csproj and b/Server/Server.csproj differ
diff --git a/Server/Workers/WatchedLibrary.cs b/Server/Workers/WatchedLibrary.cs
index f3fb60ab5..16193a55f 100644
--- a/Server/Workers/WatchedLibrary.cs
+++ b/Server/Workers/WatchedLibrary.cs
@@ -791,7 +791,7 @@ private async Task CanAccess(FileInfo file, int fileSizeDetectionInterval)
checkedAccess = true;
- using (var fs = File.Open(file.FullName, FileMode.Open))
+ using (var fs = FileOpenHelper.OpenForCheckingReadWriteAccess(file.FullName))
{
if(fs.CanRead == false)
{
@@ -924,4 +924,4 @@ private bool QueueContains(string item)
return QueuedFiles.Contains(item);
}
}
-}
+}
\ No newline at end of file
diff --git a/ServerShared/FileServices/FileDownloader.cs b/ServerShared/FileServices/FileDownloader.cs
index 1792e3714..fee18e99c 100644
--- a/ServerShared/FileServices/FileDownloader.cs
+++ b/ServerShared/FileServices/FileDownloader.cs
@@ -113,7 +113,7 @@ public async Task> DownloadFile(string path, string destinationPath
logger?.ILog("Content-Length header is not present in the response.");
}
- using FileStream fileStream = File.OpenWrite(destinationPath);
+ using FileStream fileStream = FileOpenHelper.OpenWrite_NoReadLock(destinationPath, FileMode.Create);
int bufferSize;
diff --git a/ServerShared/FileServices/FileHasher.cs b/ServerShared/FileServices/FileHasher.cs
index e05a6598b..7be3e5852 100644
--- a/ServerShared/FileServices/FileHasher.cs
+++ b/ServerShared/FileServices/FileHasher.cs
@@ -19,21 +19,21 @@ public static async Task Hash(string filePath)
var fileInfo = new FileInfo(filePath);
if (!fileInfo.Exists)
return string.Empty;
-
- using(var stream = new BufferedStream(File.OpenRead(filePath), 1200000))
+
+ using (var stream = new BufferedStream(FileOpenHelper.OpenRead_NoLocks(filePath), 1200000))
{
// The rest remains the same
var sha = SHA256.Create();
byte[] checksum = await sha.ComputeHashAsync(stream);
return BitConverter.ToString(checksum).Replace("-", string.Empty);
}
-
+
using var hasher = SHA256.Create();
byte[]? hash;
if (fileInfo.Length > 100_000_000)
{
- using var stream = File.OpenRead(filePath);
+ using var stream = FileOpenHelper.OpenRead_NoLocks(filePath);
const int chunkSize = 100_000_000; // 100MB chunks
var buffer = new byte[chunkSize];
@@ -51,7 +51,7 @@ public static async Task Hash(string filePath)
}
else
{
- await using var stream = File.OpenRead(filePath);
+ await using var stream = FileOpenHelper.OpenRead_NoLocks(filePath);
hash = await hasher.ComputeHashAsync(stream);
}
diff --git a/ServerShared/FileServices/FileUploader.cs b/ServerShared/FileServices/FileUploader.cs
index e1317cba6..a66f6498a 100644
--- a/ServerShared/FileServices/FileUploader.cs
+++ b/ServerShared/FileServices/FileUploader.cs
@@ -87,7 +87,7 @@ public async Task<(bool Success, string Error)> UploadFile(string filePath, stri
url += "?path=" + HttpUtility.UrlEncode(serverPath);
logger.ILog("Uploading file to URL: " + url);
- await using var fileStream = File.OpenRead(filePath);
+ await using var fileStream = FileOpenHelper.OpenRead_NoLocks(filePath);
var content = new StreamContent(fileStream);
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, url);
request.Headers.Add("x-executor", executorUid.ToString());
@@ -116,7 +116,7 @@ public async Task<(bool Success, string Error)> UploadFile(string filePath, stri
/// The computed SHA256 hash of the file as a byte array.
private static async Task CalculateFileHash(string filePath)
{
- await using var stream = File.OpenRead(filePath);
+ await using var stream = FileOpenHelper.OpenRead_NoLocks(filePath);
return await SHA256.Create().ComputeHashAsync(stream);
}
diff --git a/ServerShared/Helpers/FileHelper.cs b/ServerShared/Helpers/FileHelper.cs
index f42242b25..d5ea79c4d 100644
--- a/ServerShared/Helpers/FileHelper.cs
+++ b/ServerShared/Helpers/FileHelper.cs
@@ -7,7 +7,6 @@ namespace FileFlows.ServerShared.Helpers;
///
public class FileHelper
{
-
///
/// Removes illegal file/path characters from a string
///
@@ -46,7 +45,7 @@ public static string CalculateFingerprint(string file)
}
else
{
- using var stream = File.OpenRead(file);
+ using var stream = FileOpenHelper.OpenRead_NoLocks(file);
hash = hasher.ComputeHash(stream);
}
diff --git a/Shared/Helpers/FileOpenHelper.cs b/Shared/Helpers/FileOpenHelper.cs
new file mode 100644
index 000000000..463339013
--- /dev/null
+++ b/Shared/Helpers/FileOpenHelper.cs
@@ -0,0 +1,34 @@
+using System.IO;
+
+///
+/// Helper class for file open operations
+///
+public static class FileOpenHelper
+{
+ ///
+ /// Opens a file for reading without a read/write lock
+ /// This allows other applications to read/write to the file while it is being read
+ ///
+ /// the file to open
+ /// the file stream
+ public static FileStream OpenRead_NoLocks(string file)
+ => File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
+
+ ///
+ /// Opens a file for reading and writing without a read/write lock
+ /// This allows other applications to keep reading/writing the file while it's being opened
+ /// This is useful to check if CanRead/CanWrite is true on a file
+ /// Actually writing to the file with this stream might have undesired effects
+ ///
+ /// the file to open
+ /// the file stream
+ public static FileStream OpenForCheckingReadWriteAccess(string file)
+ => File.Open(file, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
+
+ ///
+ /// Opens a file for writing without a write lock
+ /// This allows other applications to read the file while it is being written to
+ ///
+ public static FileStream OpenWrite_NoReadLock(string file, FileMode fileMode)
+ => File.Open(file, fileMode, FileAccess.Write, FileShare.Read);
+}
diff --git a/Shared/Helpers/HttpHelper.cs b/Shared/Helpers/HttpHelper.cs
index 85df7db99..d078329a8 100644
--- a/Shared/Helpers/HttpHelper.cs
+++ b/Shared/Helpers/HttpHelper.cs
@@ -143,7 +143,7 @@ public static async Task DownloadFile(string url, string destination)
using HttpResponseMessage response = await Client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
await using var streamToReadFrom = await response.Content.ReadAsStreamAsync();
- await using var streamToWriteTo = File.Open(destination, FileMode.Create);
+ await using var streamToWriteTo = FileOpenHelper.OpenWrite_NoReadLock(destination, FileMode.Create);
await streamToReadFrom.CopyToAsync(streamToWriteTo);
}
catch (Exception ex)