Added LocalTime or UTC time display
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
using EonaCat.Logger.Managers;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Net;
|
||||
using System.IO.Compression;
|
||||
using EonaCat.Logger.Extensions;
|
||||
|
||||
namespace EonaCat.Logger.Test.Web
|
||||
{
|
||||
@@ -30,114 +29,64 @@ namespace EonaCat.Logger.Test.Web
|
||||
LogManager.DeleteCurrentLogFile();
|
||||
}
|
||||
|
||||
internal static IActionResult DownloadLogAsync(HttpResponse response, string logFile)
|
||||
private static string ConvertToAbsolutePath(string path)
|
||||
{
|
||||
if (IsDisabled)
|
||||
{
|
||||
response.StatusCode = (int)HttpStatusCode.BadRequest;
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check if the file exists
|
||||
if (!File.Exists(logFile))
|
||||
{
|
||||
response.StatusCode = (int)HttpStatusCode.BadRequest;
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create a cancellation token source to handle cancellation
|
||||
var cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
// Start streaming the file content to the response
|
||||
Task.Run(() => StreamFileContentAsync(response, logFile, response.Body, cancellationTokenSource.Token), cancellationTokenSource.Token);
|
||||
|
||||
// Return an empty result to indicate that streaming has started
|
||||
return new EmptyResult();
|
||||
return Path.IsPathRooted(path) ? path : Path.Combine(LogFolder, path);
|
||||
}
|
||||
|
||||
private static void DisableResponseBuffering(HttpResponse response)
|
||||
public static async Task DownloadLogAsync(HttpContext context, string logName, long limit)
|
||||
{
|
||||
var httpContext = response.HttpContext;
|
||||
var responseBodyFeature = httpContext.Features.Get<IHttpResponseBodyFeature>();
|
||||
if (responseBodyFeature != null)
|
||||
var logFileName = logName + ".log";
|
||||
|
||||
await using var fS = new FileStream(Path.Combine(ConvertToAbsolutePath(LogFolder), logFileName), FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 64 * 1024, true);
|
||||
var response = context.Response;
|
||||
|
||||
response.ContentType = "text/plain";
|
||||
response.Headers.ContentDisposition = "attachment;filename=" + logFileName;
|
||||
|
||||
if ((limit > fS.Length) || (limit < 1))
|
||||
limit = fS.Length;
|
||||
|
||||
var oFS = new OffsetStream(fS, 0, limit);
|
||||
var request = context.Request;
|
||||
Stream s;
|
||||
|
||||
string acceptEncoding = request.Headers["Accept-Encoding"];
|
||||
if (string.IsNullOrEmpty(acceptEncoding))
|
||||
{
|
||||
responseBodyFeature.DisableBuffering();
|
||||
s = response.Body;
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task StreamFileContentAsync(HttpResponse response, string filePath, Stream outputStream, CancellationToken cancellationToken)
|
||||
{
|
||||
using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 4096, FileOptions.Asynchronous | FileOptions.SequentialScan))
|
||||
else
|
||||
{
|
||||
try
|
||||
string[] acceptEncodingParts = acceptEncoding.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
||||
|
||||
if (acceptEncodingParts.Contains("br"))
|
||||
{
|
||||
// Clear the response first
|
||||
response.Clear();
|
||||
|
||||
// Disable response buffering
|
||||
DisableResponseBuffering(response);
|
||||
|
||||
// Set the response headers
|
||||
response.Headers.Add("Content-Disposition", $"attachment; filename={Path.GetFileName(filePath)}");
|
||||
response.Headers.Add("Content-Type", "application/octet-stream");
|
||||
|
||||
byte[] buffer = new byte[4096];
|
||||
int bytesRead;
|
||||
while ((bytesRead = await fileStream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) > 0)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if the response is completed
|
||||
if (response.HasStarted)
|
||||
{
|
||||
// The response is already completed, so we can't write further
|
||||
break;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await outputStream.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Do nothing, handle any specific error if needed
|
||||
}
|
||||
|
||||
// Check if the file has been modified
|
||||
if (File.GetLastWriteTimeUtc(filePath) > File.GetLastWriteTimeUtc(fileStream.Name))
|
||||
{
|
||||
// Close the existing stream and reopen the file with updated contents
|
||||
fileStream.Close();
|
||||
await StreamFileContentAsync(response, filePath, outputStream, cancellationToken).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
response.Headers.ContentEncoding = "br";
|
||||
s = new BrotliStream(response.Body, CompressionMode.Compress);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
else if (acceptEncodingParts.Contains("gzip"))
|
||||
{
|
||||
// The operation was canceled, clean up if necessary
|
||||
fileStream.Close();
|
||||
response.Headers.ContentEncoding = "gzip";
|
||||
s = new GZipStream(response.Body, CompressionMode.Compress);
|
||||
}
|
||||
finally
|
||||
else if (acceptEncodingParts.Contains("deflate"))
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
// Flush and close the output stream
|
||||
await outputStream.FlushAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (ObjectDisposedException e)
|
||||
{
|
||||
Log(e, "StreamFileContentAsync");
|
||||
}
|
||||
finally
|
||||
{
|
||||
outputStream?.Close();
|
||||
}
|
||||
response.Headers.ContentEncoding = "deflate";
|
||||
s = new DeflateStream(response.Body, CompressionMode.Compress);
|
||||
}
|
||||
else
|
||||
{
|
||||
s = response.Body;
|
||||
}
|
||||
}
|
||||
|
||||
await using (s)
|
||||
{
|
||||
await oFS.CopyToAsync(s).ConfigureAwait(false);
|
||||
|
||||
if (fS.Length > limit)
|
||||
await s.WriteAsync("\r\n####__EONACATLOGGER_TRUNCATED___####"u8.ToArray()).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user