// --------------------------------------------------------------------------------------------------------------------
//
// 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
}
}