diff options
Diffstat (limited to 'Emby.Server.Implementations/IO')
| -rw-r--r-- | Emby.Server.Implementations/IO/ExtendedFileSystemInfo.cs | 13 | ||||
| -rw-r--r-- | Emby.Server.Implementations/IO/FileRefresher.cs | 9 | ||||
| -rw-r--r-- | Emby.Server.Implementations/IO/IsoManager.cs | 1 | ||||
| -rw-r--r-- | Emby.Server.Implementations/IO/LibraryMonitor.cs | 76 | ||||
| -rw-r--r-- | Emby.Server.Implementations/IO/ManagedFileSystem.cs | 178 | ||||
| -rw-r--r-- | Emby.Server.Implementations/IO/MemoryStreamProvider.cs | 29 | ||||
| -rw-r--r-- | Emby.Server.Implementations/IO/SharpCifsFileSystem.cs | 3 | ||||
| -rw-r--r-- | Emby.Server.Implementations/IO/StreamHelper.cs | 190 |
8 files changed, 379 insertions, 120 deletions
diff --git a/Emby.Server.Implementations/IO/ExtendedFileSystemInfo.cs b/Emby.Server.Implementations/IO/ExtendedFileSystemInfo.cs new file mode 100644 index 000000000..6b08c26c9 --- /dev/null +++ b/Emby.Server.Implementations/IO/ExtendedFileSystemInfo.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Emby.Server.Implementations.IO +{ + public class ExtendedFileSystemInfo + { + public bool IsHidden { get; set; } + public bool IsReadOnly { get; set; } + public bool Exists { get; set; } + } +} diff --git a/Emby.Server.Implementations/IO/FileRefresher.cs b/Emby.Server.Implementations/IO/FileRefresher.cs index 85b8bddd2..4be30f8b7 100644 --- a/Emby.Server.Implementations/IO/FileRefresher.cs +++ b/Emby.Server.Implementations/IO/FileRefresher.cs @@ -54,7 +54,7 @@ namespace Emby.Server.Implementations.IO private void AddAffectedPath(string path) { - if (string.IsNullOrWhiteSpace(path)) + if (string.IsNullOrEmpty(path)) { throw new ArgumentNullException("path"); } @@ -67,7 +67,7 @@ namespace Emby.Server.Implementations.IO public void AddPath(string path) { - if (string.IsNullOrWhiteSpace(path)) + if (string.IsNullOrEmpty(path)) { throw new ArgumentNullException("path"); } @@ -113,7 +113,7 @@ namespace Emby.Server.Implementations.IO Path = path; AddAffectedPath(path); - if (!string.IsNullOrWhiteSpace(affectedFile)) + if (!string.IsNullOrEmpty(affectedFile)) { AddAffectedPath(affectedFile); } @@ -202,7 +202,7 @@ namespace Emby.Server.Implementations.IO // If the item has been deleted find the first valid parent that still exists while (!_fileSystem.DirectoryExists(item.Path) && !_fileSystem.FileExists(item.Path)) { - item = item.IsOwnedItem ? item.GetOwner() : item.GetParent(); + item = item.GetOwner() ?? item.GetParent(); if (item == null) { @@ -231,7 +231,6 @@ namespace Emby.Server.Implementations.IO { _disposed = true; DisposeTimer(); - GC.SuppressFinalize(this); } } } diff --git a/Emby.Server.Implementations/IO/IsoManager.cs b/Emby.Server.Implementations/IO/IsoManager.cs index dc0b9e122..903d5f301 100644 --- a/Emby.Server.Implementations/IO/IsoManager.cs +++ b/Emby.Server.Implementations/IO/IsoManager.cs @@ -70,7 +70,6 @@ namespace Emby.Server.Implementations.IO { mounter.Dispose(); } - GC.SuppressFinalize(this); } } } diff --git a/Emby.Server.Implementations/IO/LibraryMonitor.cs b/Emby.Server.Implementations/IO/LibraryMonitor.cs index a2abb2a5c..00fe447f0 100644 --- a/Emby.Server.Implementations/IO/LibraryMonitor.cs +++ b/Emby.Server.Implementations/IO/LibraryMonitor.cs @@ -266,7 +266,7 @@ namespace Emby.Server.Implementations.IO /// <exception cref="System.ArgumentNullException">path</exception> private static bool ContainsParentFolder(IEnumerable<string> lst, string path) { - if (string.IsNullOrWhiteSpace(path)) + if (string.IsNullOrEmpty(path)) { throw new ArgumentNullException("path"); } @@ -304,6 +304,12 @@ namespace Emby.Server.Implementations.IO } } + if (_environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Android) + { + // causing crashing + return; + } + // Already being watched if (_fileSystemWatchers.ContainsKey(path)) { @@ -320,11 +326,7 @@ namespace Emby.Server.Implementations.IO IncludeSubdirectories = true }; - if (_environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows || - _environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.OSX) - { - newWatcher.InternalBufferSize = 32767; - } + newWatcher.InternalBufferSize = 65536; newWatcher.NotifyFilter = NotifyFilters.CreationTime | NotifyFilters.DirectoryName | @@ -337,7 +339,6 @@ namespace Emby.Server.Implementations.IO newWatcher.Deleted += watcher_Changed; newWatcher.Renamed += watcher_Changed; newWatcher.Changed += watcher_Changed; - newWatcher.Error += watcher_Error; if (_fileSystemWatchers.TryAdd(path, newWatcher)) @@ -347,7 +348,7 @@ namespace Emby.Server.Implementations.IO } else { - newWatcher.Dispose(); + DisposeWatcher(newWatcher, false); } } @@ -368,15 +369,14 @@ namespace Emby.Server.Implementations.IO if (_fileSystemWatchers.TryGetValue(path, out watcher)) { - DisposeWatcher(watcher); + DisposeWatcher(watcher, true); } } /// <summary> /// Disposes the watcher. /// </summary> - /// <param name="watcher">The watcher.</param> - private void DisposeWatcher(FileSystemWatcher watcher) + private void DisposeWatcher(FileSystemWatcher watcher, bool removeFromList) { try { @@ -384,16 +384,37 @@ namespace Emby.Server.Implementations.IO { Logger.Info("Stopping directory watching for path {0}", watcher.Path); - watcher.EnableRaisingEvents = false; + watcher.Created -= watcher_Changed; + watcher.Deleted -= watcher_Changed; + watcher.Renamed -= watcher_Changed; + watcher.Changed -= watcher_Changed; + watcher.Error -= watcher_Error; + + try + { + watcher.EnableRaisingEvents = false; + } + catch (InvalidOperationException) + { + // Seeing this under mono on linux sometimes + // Collection was modified; enumeration operation may not execute. + } } } + catch (NotImplementedException) + { + // the dispose method on FileSystemWatcher is sometimes throwing NotImplementedException on Xamarin Android + } catch { } finally { - RemoveWatcherFromList(watcher); + if (removeFromList) + { + RemoveWatcherFromList(watcher); + } } } @@ -420,7 +441,7 @@ namespace Emby.Server.Implementations.IO Logger.ErrorException("Error in Directory watcher for: " + dw.Path, ex); - DisposeWatcher(dw); + DisposeWatcher(dw, true); } /// <summary> @@ -452,7 +473,7 @@ namespace Emby.Server.Implementations.IO } var filename = Path.GetFileName(path); - + var monitorPath = !string.IsNullOrEmpty(filename) && !_alwaysIgnoreFiles.Contains(filename, StringComparer.OrdinalIgnoreCase) && !_alwaysIgnoreExtensions.Contains(Path.GetExtension(path) ?? string.Empty, StringComparer.OrdinalIgnoreCase) && @@ -466,13 +487,13 @@ namespace Emby.Server.Implementations.IO { if (_fileSystem.AreEqual(i, path)) { - Logger.Debug("Ignoring change to {0}", path); + //Logger.Debug("Ignoring change to {0}", path); return true; } if (_fileSystem.ContainsSubPath(i, path)) { - Logger.Debug("Ignoring change to {0}", path); + //Logger.Debug("Ignoring change to {0}", path); return true; } @@ -482,7 +503,7 @@ namespace Emby.Server.Implementations.IO { if (_fileSystem.AreEqual(parent, path)) { - Logger.Debug("Ignoring change to {0}", path); + //Logger.Debug("Ignoring change to {0}", path); return true; } } @@ -561,22 +582,7 @@ namespace Emby.Server.Implementations.IO foreach (var watcher in _fileSystemWatchers.Values.ToList()) { - watcher.Created -= watcher_Changed; - watcher.Deleted -= watcher_Changed; - watcher.Renamed -= watcher_Changed; - watcher.Changed -= watcher_Changed; - - try - { - watcher.EnableRaisingEvents = false; - } - catch (InvalidOperationException) - { - // Seeing this under mono on linux sometimes - // Collection was modified; enumeration operation may not execute. - } - - watcher.Dispose(); + DisposeWatcher(watcher, false); } _fileSystemWatchers.Clear(); @@ -612,7 +618,6 @@ namespace Emby.Server.Implementations.IO { _disposed = true; Dispose(true); - GC.SuppressFinalize(this); } /// <summary> @@ -644,7 +649,6 @@ namespace Emby.Server.Implementations.IO public void Dispose() { - GC.SuppressFinalize(this); } } } diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs index c8e4031a9..66d7802c6 100644 --- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs +++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs @@ -20,27 +20,57 @@ namespace Emby.Server.Implementations.IO private readonly bool _supportsAsyncFileStreams; private char[] _invalidFileNameChars; private readonly List<IShortcutHandler> _shortcutHandlers = new List<IShortcutHandler>(); - private bool EnableFileSystemRequestConcat; + private bool EnableSeparateFileAndDirectoryQueries; private string _tempPath; private SharpCifsFileSystem _sharpCifsFileSystem; private IEnvironmentInfo _environmentInfo; + private bool _isEnvironmentCaseInsensitive; - public ManagedFileSystem(ILogger logger, IEnvironmentInfo environmentInfo, string tempPath) + private string _defaultDirectory; + + public ManagedFileSystem(ILogger logger, IEnvironmentInfo environmentInfo, string defaultDirectory, string tempPath, bool enableSeparateFileAndDirectoryQueries) { Logger = logger; _supportsAsyncFileStreams = true; _tempPath = tempPath; _environmentInfo = environmentInfo; + _defaultDirectory = defaultDirectory; - // On Linux, this needs to be true or symbolic links are ignored - EnableFileSystemRequestConcat = environmentInfo.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.Windows && - environmentInfo.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.OSX; + // On Linux with mono, this needs to be true or symbolic links are ignored + EnableSeparateFileAndDirectoryQueries = enableSeparateFileAndDirectoryQueries; SetInvalidFileNameChars(environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows); _sharpCifsFileSystem = new SharpCifsFileSystem(environmentInfo.OperatingSystem); + + _isEnvironmentCaseInsensitive = environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows; + } + + public string DefaultDirectory + { + get + { + var value = _defaultDirectory; + + if (!string.IsNullOrEmpty(value)) + { + try + { + if (DirectoryExists(value)) + { + return value; + } + } + catch + { + + } + } + + return null; + } } public void AddShortcutHandler(IShortcutHandler handler) @@ -56,11 +86,9 @@ namespace Emby.Server.Implementations.IO } else { - // GetInvalidFileNameChars is less restrictive in Linux/Mac than Windows, this mimic Windows behavior for mono under Linux/Mac. - _invalidFileNameChars = new char[41] { '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', - '\x08', '\x09', '\x0A', '\x0B', '\x0C', '\x0D', '\x0E', '\x0F', '\x10', '\x11', '\x12', - '\x13', '\x14', '\x15', '\x16', '\x17', '\x18', '\x19', '\x1A', '\x1B', '\x1C', '\x1D', - '\x1E', '\x1F', '\x22', '\x3C', '\x3E', '\x7C', ':', '*', '?', '\\', '/' }; + // Be consistent across platforms because the windows server will fail to query network shares that don't follow windows conventions + // https://referencesource.microsoft.com/#mscorlib/system/io/path.cs + _invalidFileNameChars = new char[] { '\"', '<', '>', '|', '\0', (Char)1, (Char)2, (Char)3, (Char)4, (Char)5, (Char)6, (Char)7, (Char)8, (Char)9, (Char)10, (Char)11, (Char)12, (Char)13, (Char)14, (Char)15, (Char)16, (Char)17, (Char)18, (Char)19, (Char)20, (Char)21, (Char)22, (Char)23, (Char)24, (Char)25, (Char)26, (Char)27, (Char)28, (Char)29, (Char)30, (Char)31, ':', '*', '?', '\\', '/' }; } } @@ -118,6 +146,49 @@ namespace Emby.Server.Implementations.IO return null; } + public string MakeAbsolutePath(string folderPath, string filePath) + { + if (String.IsNullOrWhiteSpace(filePath)) return filePath; + + if (filePath.Contains(@"://")) return filePath; //stream + if (filePath.Length > 3 && filePath[1] == ':' && filePath[2] == '/') return filePath; //absolute local path + + // unc path + if (filePath.StartsWith("\\\\")) + { + return filePath; + } + + var firstChar = filePath[0]; + if (firstChar == '/') + { + // For this we don't really know. + return filePath; + } + if (firstChar == '\\') //relative path + { + filePath = filePath.Substring(1); + } + try + { + string path = System.IO.Path.Combine(folderPath, filePath); + path = System.IO.Path.GetFullPath(path); + return path; + } + catch (ArgumentException ex) + { + return filePath; + } + catch (PathTooLongException) + { + return filePath; + } + catch (NotSupportedException) + { + return filePath; + } + } + /// <summary> /// Creates the shortcut. /// </summary> @@ -162,11 +233,6 @@ namespace Emby.Server.Implementations.IO /// <see cref="FileSystemMetadata.IsDirectory"/> property will be set to true and all other properties will reflect the properties of the directory.</remarks> public FileSystemMetadata GetFileSystemInfo(string path) { - if (string.IsNullOrEmpty(path)) - { - throw new ArgumentNullException("path"); - } - if (_sharpCifsFileSystem.IsEnabledForPath(path)) { return _sharpCifsFileSystem.GetFileSystemInfo(path); @@ -207,11 +273,6 @@ namespace Emby.Server.Implementations.IO /// <para>For automatic handling of files <b>and</b> directories, use <see cref="GetFileSystemInfo"/>.</para></remarks> public FileSystemMetadata GetFileInfo(string path) { - if (string.IsNullOrEmpty(path)) - { - throw new ArgumentNullException("path"); - } - if (_sharpCifsFileSystem.IsEnabledForPath(path)) { return _sharpCifsFileSystem.GetFileInfo(path); @@ -232,11 +293,6 @@ namespace Emby.Server.Implementations.IO /// <para>For automatic handling of files <b>and</b> directories, use <see cref="GetFileSystemInfo"/>.</para></remarks> public FileSystemMetadata GetDirectoryInfo(string path) { - if (string.IsNullOrEmpty(path)) - { - throw new ArgumentNullException("path"); - } - if (_sharpCifsFileSystem.IsEnabledForPath(path)) { return _sharpCifsFileSystem.GetDirectoryInfo(path); @@ -258,10 +314,12 @@ namespace Emby.Server.Implementations.IO if (result.Exists) { - var attributes = info.Attributes; - result.IsDirectory = info is DirectoryInfo || (attributes & FileAttributes.Directory) == FileAttributes.Directory; - result.IsHidden = (attributes & FileAttributes.Hidden) == FileAttributes.Hidden; - result.IsReadOnly = (attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly; + result.IsDirectory = info is DirectoryInfo || (info.Attributes & FileAttributes.Directory) == FileAttributes.Directory; + + //if (!result.IsDirectory) + //{ + // result.IsHidden = (info.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden; + //} var fileInfo = info as FileInfo; if (fileInfo != null) @@ -281,6 +339,25 @@ namespace Emby.Server.Implementations.IO return result; } + private ExtendedFileSystemInfo GetExtendedFileSystemInfo(string path) + { + var result = new ExtendedFileSystemInfo(); + + var info = new FileInfo(path); + + if (info.Exists) + { + result.Exists = true; + + var attributes = info.Attributes; + + result.IsHidden = (attributes & FileAttributes.Hidden) == FileAttributes.Hidden; + result.IsReadOnly = (attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly; + } + + return result; + } + /// <summary> /// The space char /// </summary> @@ -294,11 +371,6 @@ namespace Emby.Server.Implementations.IO /// <exception cref="System.ArgumentNullException">filename</exception> public string GetValidFilename(string filename) { - if (string.IsNullOrEmpty(filename)) - { - throw new ArgumentNullException("filename"); - } - var builder = new StringBuilder(filename); foreach (var c in _invalidFileNameChars) @@ -484,7 +556,7 @@ namespace Emby.Server.Implementations.IO return; } - var info = GetFileInfo(path); + var info = GetExtendedFileSystemInfo(path); if (info.Exists && info.IsHidden != isHidden) { @@ -514,7 +586,7 @@ namespace Emby.Server.Implementations.IO return; } - var info = GetFileInfo(path); + var info = GetExtendedFileSystemInfo(path); if (info.Exists && info.IsReadOnly != isReadOnly) { @@ -544,7 +616,7 @@ namespace Emby.Server.Implementations.IO return; } - var info = GetFileInfo(path); + var info = GetExtendedFileSystemInfo(path); if (!info.Exists) { @@ -720,11 +792,6 @@ namespace Emby.Server.Implementations.IO public bool IsPathFile(string path) { - if (string.IsNullOrWhiteSpace(path)) - { - throw new ArgumentNullException("path"); - } - // Cannot use Path.IsPathRooted because it returns false under mono when using windows-based paths, e.g. C:\\ if (_sharpCifsFileSystem.IsEnabledForPath(path)) @@ -822,7 +889,7 @@ namespace Emby.Server.Implementations.IO // On linux and osx the search pattern is case sensitive // If we're OK with case-sensitivity, and we're only filtering for one extension, then use the native method - if (enableCaseSensitiveExtensions && extensions != null && extensions.Length == 1) + if ((enableCaseSensitiveExtensions || _isEnvironmentCaseInsensitive) && extensions != null && extensions.Length == 1) { return ToMetadata(new DirectoryInfo(path).EnumerateFiles("*" + extensions[0], searchOption)); } @@ -855,7 +922,7 @@ namespace Emby.Server.Implementations.IO var directoryInfo = new DirectoryInfo(path); var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly; - if (EnableFileSystemRequestConcat) + if (EnableSeparateFileAndDirectoryQueries) { return ToMetadata(directoryInfo.EnumerateDirectories("*", searchOption)) .Concat(ToMetadata(directoryInfo.EnumerateFiles("*", searchOption))); @@ -897,9 +964,28 @@ namespace Emby.Server.Implementations.IO return File.OpenRead(path); } + private void CopyFileUsingStreams(string source, string target, bool overwrite) + { + using (var sourceStream = OpenRead(source)) + { + using (var targetStream = GetFileStream(target, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read)) + { + sourceStream.CopyTo(targetStream); + } + } + } + public void CopyFile(string source, string target, bool overwrite) { - if (_sharpCifsFileSystem.IsEnabledForPath(source)) + var enableSharpCifsForSource = _sharpCifsFileSystem.IsEnabledForPath(source); + + if (enableSharpCifsForSource != _sharpCifsFileSystem.IsEnabledForPath(target)) + { + CopyFileUsingStreams(source, target, overwrite); + return; + } + + if (enableSharpCifsForSource) { _sharpCifsFileSystem.CopyFile(source, target, overwrite); return; @@ -1033,7 +1119,7 @@ namespace Emby.Server.Implementations.IO // On linux and osx the search pattern is case sensitive // If we're OK with case-sensitivity, and we're only filtering for one extension, then use the native method - if (enableCaseSensitiveExtensions && extensions != null && extensions.Length == 1) + if ((enableCaseSensitiveExtensions || _isEnvironmentCaseInsensitive) && extensions != null && extensions.Length == 1) { return Directory.EnumerateFiles(path, "*" + extensions[0], searchOption); } diff --git a/Emby.Server.Implementations/IO/MemoryStreamProvider.cs b/Emby.Server.Implementations/IO/MemoryStreamProvider.cs deleted file mode 100644 index e9ecb7e44..000000000 --- a/Emby.Server.Implementations/IO/MemoryStreamProvider.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.IO; -using MediaBrowser.Model.IO; - -namespace Emby.Server.Implementations.IO -{ - public class MemoryStreamProvider : IMemoryStreamFactory - { - public MemoryStream CreateNew() - { - return new MemoryStream(); - } - - public MemoryStream CreateNew(int capacity) - { - return new MemoryStream(capacity); - } - - public MemoryStream CreateNew(byte[] buffer) - { - return new MemoryStream(buffer); - } - - public bool TryGetBuffer(MemoryStream stream, out byte[] buffer) - { - buffer = stream.GetBuffer(); - return true; - } - } -} diff --git a/Emby.Server.Implementations/IO/SharpCifsFileSystem.cs b/Emby.Server.Implementations/IO/SharpCifsFileSystem.cs index 0e1f6bb00..a48543bc7 100644 --- a/Emby.Server.Implementations/IO/SharpCifsFileSystem.cs +++ b/Emby.Server.Implementations/IO/SharpCifsFileSystem.cs @@ -136,9 +136,6 @@ namespace Emby.Server.Implementations.IO if (result.Exists) { result.IsDirectory = info.IsDirectory(); - result.IsHidden = info.IsHidden(); - - result.IsReadOnly = !info.CanWrite(); if (info.IsFile()) { diff --git a/Emby.Server.Implementations/IO/StreamHelper.cs b/Emby.Server.Implementations/IO/StreamHelper.cs new file mode 100644 index 000000000..48a5063e8 --- /dev/null +++ b/Emby.Server.Implementations/IO/StreamHelper.cs @@ -0,0 +1,190 @@ +using System.IO; +using System.Threading; +using System; +using System.Threading.Tasks; +using MediaBrowser.Model.IO; + +namespace Emby.Server.Implementations.IO +{ + public class StreamHelper : IStreamHelper + { + public async Task CopyToAsync(Stream source, Stream destination, int bufferSize, Action onStarted, CancellationToken cancellationToken) + { + byte[] buffer = new byte[bufferSize]; + int read; + while ((read = source.Read(buffer, 0, buffer.Length)) != 0) + { + cancellationToken.ThrowIfCancellationRequested(); + + await destination.WriteAsync(buffer, 0, read).ConfigureAwait(false); + + if (onStarted != null) + { + onStarted(); + onStarted = null; + } + } + } + + public async Task CopyToAsync(Stream source, Stream destination, int bufferSize, int emptyReadLimit, CancellationToken cancellationToken) + { + byte[] buffer = new byte[bufferSize]; + + if (emptyReadLimit <= 0) + { + int read; + while ((read = source.Read(buffer, 0, buffer.Length)) != 0) + { + cancellationToken.ThrowIfCancellationRequested(); + + await destination.WriteAsync(buffer, 0, read).ConfigureAwait(false); + } + + return; + } + + var eofCount = 0; + + while (eofCount < emptyReadLimit) + { + cancellationToken.ThrowIfCancellationRequested(); + + var bytesRead = source.Read(buffer, 0, buffer.Length); + + if (bytesRead == 0) + { + eofCount++; + await Task.Delay(50, cancellationToken).ConfigureAwait(false); + } + else + { + eofCount = 0; + + await destination.WriteAsync(buffer, 0, bytesRead).ConfigureAwait(false); + } + } + } + + const int StreamCopyToBufferSize = 81920; + public async Task<int> CopyToAsync(Stream source, Stream destination, CancellationToken cancellationToken) + { + var array = new byte[StreamCopyToBufferSize]; + int bytesRead; + int totalBytesRead = 0; + + while ((bytesRead = await source.ReadAsync(array, 0, array.Length, cancellationToken).ConfigureAwait(false)) != 0) + { + var bytesToWrite = bytesRead; + + if (bytesToWrite > 0) + { + await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToWrite), cancellationToken).ConfigureAwait(false); + + totalBytesRead += bytesRead; + } + } + + return totalBytesRead; + } + + public async Task<int> CopyToAsyncWithSyncRead(Stream source, Stream destination, CancellationToken cancellationToken) + { + var array = new byte[StreamCopyToBufferSize]; + int bytesRead; + int totalBytesRead = 0; + + while ((bytesRead = source.Read(array, 0, array.Length)) != 0) + { + var bytesToWrite = bytesRead; + + if (bytesToWrite > 0) + { + await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToWrite), cancellationToken).ConfigureAwait(false); + + totalBytesRead += bytesRead; + } + } + + return totalBytesRead; + } + + public async Task CopyToAsyncWithSyncRead(Stream source, Stream destination, long copyLength, CancellationToken cancellationToken) + { + var array = new byte[StreamCopyToBufferSize]; + int bytesRead; + + while ((bytesRead = source.Read(array, 0, array.Length)) != 0) + { + var bytesToWrite = Math.Min(bytesRead, copyLength); + + if (bytesToWrite > 0) + { + await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToWrite), cancellationToken).ConfigureAwait(false); + } + + copyLength -= bytesToWrite; + + if (copyLength <= 0) + { + break; + } + } + } + + public async Task CopyToAsync(Stream source, Stream destination, long copyLength, CancellationToken cancellationToken) + { + var array = new byte[StreamCopyToBufferSize]; + int bytesRead; + + while ((bytesRead = await source.ReadAsync(array, 0, array.Length, cancellationToken).ConfigureAwait(false)) != 0) + { + var bytesToWrite = Math.Min(bytesRead, copyLength); + + if (bytesToWrite > 0) + { + await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToWrite), cancellationToken).ConfigureAwait(false); + } + + copyLength -= bytesToWrite; + + if (copyLength <= 0) + { + break; + } + } + } + + public async Task CopyUntilCancelled(Stream source, Stream target, int bufferSize, CancellationToken cancellationToken) + { + byte[] buffer = new byte[bufferSize]; + + while (!cancellationToken.IsCancellationRequested) + { + var bytesRead = await CopyToAsyncInternal(source, target, buffer, cancellationToken).ConfigureAwait(false); + + //var position = fs.Position; + //_logger.Debug("Streamed {0} bytes to position {1} from file {2}", bytesRead, position, path); + + if (bytesRead == 0) + { + await Task.Delay(100).ConfigureAwait(false); + } + } + } + + private static async Task<int> CopyToAsyncInternal(Stream source, Stream destination, byte[] buffer, CancellationToken cancellationToken) + { + int bytesRead; + int totalBytesRead = 0; + + while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0) + { + await destination.WriteAsync(buffer, 0, bytesRead).ConfigureAwait(false); + + totalBytesRead += bytesRead; + } + + return totalBytesRead; + } + } +} |
