// -------------------------------------------------------------------------------------------------------------------- // // Copyright (c) VRMADA, All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Threading; using System.Threading.Tasks; using UltimateXR.Exceptions; using UltimateXR.Extensions.Unity.IO; using UnityEngine; using UnityEngine.Networking; namespace UltimateXR.Extensions.System.IO { /// /// File extensions. /// public static class FileExt { #region Public Methods /// /// Reads bytes from a file asynchronously. /// Multiple file locations are supported: /// /// Files in /// Files in an http:// location /// Files in a file:// location /// /// All other Uris will be considered file paths and the file:// location will be added. /// /// File full path to be opened for reading /// /// Optional cancellation token, to be able to cancel the asynchronous operation /// /// /// Bytes read /// /// /// UnityWebRequest.Get() is used internally to perform the actual /// reading /// /// /// Task canceled using /// /// /// The file specified in was not found. /// /// /// is in an invalid format. /// /// /// An I/O error occurred while opening the file. /// /// /// The stream is currently in use by a previous read operation. /// public static async Task Read(string uri, CancellationToken ct = default) { ct.ThrowIfCancellationRequested(); uri.ThrowIfNullOrWhitespace(nameof(uri)); if (UnityWebRequestExt.IsUwrUri(uri)) { try { return await UnityWebRequestExt.ReadBytesAsync(uri, ct); } catch (UwrException e) { throw new FileNotFoundException(e.Message, uri, e); } } List bytes = new List(); byte[] buffer = new byte[0x1000]; using (var fs = new FileStream(uri, FileMode.Open, FileAccess.Read, FileShare.Read, buffer.Length, true)) { while (await fs.ReadAsync(buffer, 0, buffer.Length, ct).ConfigureAwait(false) != 0) { bytes.AddRange(buffer); } } return bytes.ToArray(); } /// /// Reads text from a file asynchronously. /// Multiple file locations are supported: /// /// Files in /// Files in an http:// location /// Files in a file:// location /// /// All other Uris will be considered file paths and the file:// location will be added. /// /// File location /// Optional file encoding /// Optional cancellation token, to cancel the asynchronous operation /// A pair describing a boolean success value and the text read /// /// UnityWebRequest.Get() is used internally to perform the actual /// reading /// public static async Task<(bool success, string text)> TryReadText(string uri, Encoding encoding = default, CancellationToken ct = default) { (bool success, string text) result; try { result.text = await ReadText(uri, encoding, ct).ConfigureAwait(false); result.success = true; } catch { result.text = null; result.success = false; } return result; } /// /// Reads text from a file asynchronously. /// Multiple file locations are supported: /// /// Files in /// Files in an http:// location /// Files in a file:// location /// /// All other Uris will be considered file paths and the file:// location will be added. /// /// File full path to be opened for reading /// Optional file encoding /// Optional cancellation token, to cancel the asynchronous operation /// Text content of the file or if not found. /// /// UnityWebRequest.Get() is used internally to perform the actual /// reading /// /// /// Task canceled using /// /// /// is in an invalid format. /// /// /// An I/O error occurred while opening the file. /// /// /// The stream is currently in use by a previous read operation. /// public static async Task ReadText(string uri, Encoding encoding = default, CancellationToken ct = default) { ct.ThrowIfCancellationRequested(); uri.ThrowIfNullOrWhitespace(nameof(uri)); encoding ??= DefaultEncoding; if (UnityWebRequestExt.IsUwrUri(uri)) { try { return await UnityWebRequestExt.ReadTextAsync(uri, ct); } catch (UwrException e) { throw new FileNotFoundException(e.Message, uri, e); } } if (!File.Exists(uri)) { throw new FileNotFoundException("File does not exist", uri); } using StreamReader sr = new StreamReader(uri, encoding, true); return await sr.ReadToEndAsync().ConfigureAwait(false); } /// /// Asynchronously writes an of to a file at . /// /// File content as of /// File full path to be opened for writing /// Optional cancellation token, to cancel the asynchronous operation /// An awaitable writing /// /// An I/O error occurred while creating the file. /// /// /// is a zero-length string, contains only white space, or /// contains one or more invalid characters. You can query for invalid characters by using the /// method. /// /// /// The parameter is longer than the system-defined maximum length. /// /// /// The caller does not have the required permission. /// /// /// Task canceled using /// public static async Task Write(byte[] bytes, string path, CancellationToken ct = default) { ct.ThrowIfCancellationRequested(); path.ThrowIfNullOrWhitespace(nameof(path)); using var stream = new MemoryStream(bytes); await Write(path, stream, ct); } /// /// Asynchronously writes the content of an to a file at . /// /// File full path to be opened for writing /// to be written into a file. /// Optional cancellation token, to cancel the asynchronous operation /// An awaitable writing /// /// An I/O error occurred while creating the file. /// /// /// is a zero-length string, contains only white space, or /// contains one or more invalid characters. You can query for invalid characters by using the /// method. /// /// /// The parameter is longer than the system-defined maximum length. /// /// /// does not support reading, or the destination stream does not support writing. /// /// /// The caller does not have the required permission. /// /// /// Task canceled using /// public static Task Write(string path, Stream sourceStream, CancellationToken ct = default) { ct.ThrowIfCancellationRequested(); path.ThrowIfNullOrWhitespace(nameof(path)); sourceStream.ThrowIfNull(nameof(sourceStream)); string dirName = Path.GetDirectoryName(path); const int bufferSize = 81920; async Task WriteInternal() { if (dirName != null) { Directory.CreateDirectory(dirName); } using FileStream outputStream = new FileStream(path, FileMode.Create, FileAccess.ReadWrite, FileShare.None, bufferSize, FileOptions.Asynchronous); await sourceStream.CopyToAsync(outputStream, bufferSize, ct).ConfigureAwait(false); } // Creating the new file has a performance impact that requires a new thread. return Task.Run(WriteInternal, ct); } /// /// Asynchronously writes text to a file location. /// /// File full path to be opened for writing /// Text to write /// Optional file encoding /// Optional boolean telling whether to append or override. Default behaviour is to override. /// Optional cancellation token, to cancel the asynchronous operation /// Awaitable task /// /// Task canceled using /// public static Task WriteText(string path, string text, Encoding encoding = default, bool append = false, CancellationToken ct = default) { ct.ThrowIfCancellationRequested(); path.ThrowIfNullOrWhitespace(nameof(path)); encoding ??= DefaultEncoding; string dirName = Path.GetDirectoryName(path); async Task WriteInternal() { if (dirName != null) { Directory.CreateDirectory(dirName); } using StreamWriter sw = new StreamWriter(path, append, encoding); await sw.WriteAsync(text).ConfigureAwait(false); } // Creating the new file has a performance impact that requires a new thread. return Task.Run(WriteInternal, ct); } #endregion #region Private Types & Data private static readonly Encoding DefaultEncoding = new UTF8Encoding(false); #endregion } }