I've been using a logging middleware to record errors along with user requests in the database. However, due to parallel image uploads and compression using Magick.NET, I encountered an error. What should I do?
code :
upload parallel image :
`
public async Task<string> UploadFileAsync(IFormFile file, string folderPath, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(file);
try
{
using (var compressedStream = await _compressProviderService.CompressMedia(file.OpenReadStream(), cancellationToken))
{
var poa = new PutObjectArgs()
.WithBucket(_config.Minio.BucketName)
.WithContentType(file.ContentType)
.WithObject($"{folderPath}/{file.FileName}")
.WithStreamData(compressedStream)
.WithObjectSize(compressedStream.Length);
await _minioClient.PutObjectAsync(poa, cancellationToken).ConfigureAwait(false);
var result =
$"{_config.Minio.MinioEndpointExternal}/{_config.Minio.BucketName}/{folderPath}/{file.FileName}";
return GetMediaUrl(result);
}
}
catch (Exception ex)
{
_logger.CompileLog(ex, LogLevel.Error, $"upload file exception: {ex.Message}");
throw;
}
}
public async Task<List<string>> UploadFilesAsync(IList<IFormFile> files, string folderPath, CancellationToken cancellationToken)
{
if (files == null || files.Count == 0)
{
throw new ArgumentNullException(nameof(files), "Files list is empty or null.");
}
var uploadedUrls = new List<string>();
var options = new ParallelOptions()
{
MaxDegreeOfParallelism = 10
};
try
{
await Parallel.ForEachAsync(files, options, async (file, cancellationToken) =>
{
var url = await UploadFileAsync(file, folderPath, cancellationToken);
uploadedUrls.Add(GetMediaUrl(url));
});
return uploadedUrls;
}
catch (Exception ex)
{
_logger.CompileLog(ex, LogLevel.Error, $"Upload files exception: {ex.Message}");
throw;
}
}
`
compress method :
public class CompressProviderService : ICompressProviderService
{
public async Task<MemoryStream> CompressMedia(Stream stream, CancellationToken cancellationToken)
{
stream.Seek(0, SeekOrigin.Begin);
using (var image = new MagickImage(stream))
{
image.Format = MagickFormat.Jpeg;
image.Resize(1600, 900);
image.Quality = 70;
var compressedStream = new MemoryStream();
await image.WriteAsync(compressedStream, cancellationToken);
compressedStream.Position = 0;
return compressedStream;
}
}
}
middleware :
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Common.Core.Model;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using SharedKernel.Domain.Services;
using SharedKernel.Logger;
namespace Common.Core.Service;
public sealed class CustomExceptionMiddleware
{
private readonly IConfiguration _configuration;
private readonly IWebHostEnvironment _env;
private readonly ILogger<CustomExceptionMiddleware> _logger;
private readonly RequestDelegate _next;
private readonly bool _useStringEnumConverter;
public CustomExceptionMiddleware(RequestDelegate next, IWebHostEnvironment env,
ILogger<CustomExceptionMiddleware> logger, IConfiguration configuration,
bool useStringEnumConverter)
{
_next = next ?? throw new ArgumentNullException(nameof(next));
_env = env;
_logger = logger;
_configuration = configuration;
_useStringEnumConverter = useStringEnumConverter;
}
public async Task InvokeAsync(HttpContext context)
{
try
{
context.Request.EnableBuffering();
await _next(context).ConfigureAwait(false);
}
catch (CustomException e)
{
if (context.Response.HasStarted)
throw;
await HandleCustomExceptionAsync(context, e).ConfigureAwait(false);
}
catch (Exception exc)
{
if (context.Response.HasStarted)
{
_logger.CompileLog(exc, LogLevel.Error, exc.Message);
throw;
}
await HandleExceptionAsync(context, exc).ConfigureAwait(false);
}
}
private async Task HandleCustomExceptionAsync(HttpContext context, CustomException e)
{
var traceId = context.GenerateTraceId();
var error = new ErrorDto(e.Error.Error.WithTraceIdentifier(traceId));
SetResponse(context, e.Error.Error.HttpStatusCode);
var errorHeaders = e.Error.Error.Headers;
if (!string.IsNullOrEmpty(errorHeaders))
{
try
{
var headersList = errorHeaders.Split(';').Select(header => header.Split('=')).ToList();
foreach (var headerPair in headersList)
{
context.Response.Headers.Append(headerPair[0], headerPair[1]);
}
}
catch
{
await HandleServerErrorAsync(context, traceId, $"invalid custom header: {errorHeaders}. correct header: f=10;h=9").ConfigureAwait(false);
return;
}
}
await WriteResponseAsync(context, error).ConfigureAwait(false);
}
private async Task HandleExceptionAsync(HttpContext context, Exception exc)
{
var traceId = context.GenerateTraceId();
var message = "Internal Server Error";
context.Response.StatusCode = 500;
context.Response.ContentType = "application/json";
context.Response.Headers.Append("exception", "Internal Server Error");
var details = new List<ValidationError>();
var isShowFullError = _configuration.GetValue("ExceptionShowFullError", false);
var requestBody = await GetRequestBodyAsync(context.Request);
var stringBuilder = new StringBuilder();
stringBuilder.Append("Internal Server Error ").AppendFormat(CultureInfo.InvariantCulture, "#Message: {0}", exc.Message).AppendFormat(CultureInfo.InvariantCulture, ", #RequestBody: {0}", requestBody);
var log = stringBuilder.ToString();
if (_env.IsDevelopment() || isShowFullError)
{
message = $"#Message: {exc.Message}, #StackTrace: {exc.StackTrace}, #RequestBody: {requestBody}";
var exception = exc.InnerException;
while (exception != null)
{
details.Add(new ValidationError(
"ServerError",
$"#Message: {exception.Message} #StackTrace: {exception.StackTrace}, #RequestPath: {context.Request.Path}, #RequestBody: {requestBody}"
));
exception = exception.InnerException;
}
}
var jsonLog = log.ToJson();
var errorDto = new ErrorDto(new ValidationError("ServerError", message, details, traceId));
await WriteResponseAsync(context, errorDto).ConfigureAwait(false);
_logger.CompileLog(exc, LogLevel.Error, jsonLog);
}
private static async Task<string> GetRequestBodyAsync(HttpRequest request)
{
string body;
if (request.QueryString.HasValue)
{
var queryStringDict = QueryHelpers.ParseQuery(request.QueryString.Value);
var queryParams = queryStringDict.SelectMany(x => x.Value, (col, value) => new KeyValuePair<string, string>(col.Key, value)).ToDictionary(x => x.Key, x => x.Value);
body = queryParams.ToJson();
}
else if (request.HasFormContentType)
{
var formFields = request.Form.ToDictionary(x => x.Key, x => x.Value);
body = formFields.ToJson();
body = body.Replace("\"", ",", StringComparison.CurrentCulture);
body = body.Replace(",,", " ", StringComparison.CurrentCulture);
body = body.Replace(",", "", StringComparison.CurrentCulture);
}
else
{
using var stream = new StreamReader(request.Body);
stream.BaseStream.Seek(0, SeekOrigin.Begin);
body = await stream.ReadToEndAsync();
body = body.Replace("\r\n", "", StringComparison.OrdinalIgnoreCase);
request.Body.Position = 0;
}
if (body.Length > 512)
{
body = body[..512];
}
return body;
}
private static void SetResponse(HttpContext context, int httpStatusCode)
{
context.Response.StatusCode = httpStatusCode == 0 ? 400 : httpStatusCode;
context.Response.ContentType = "application/json";
}
private async Task HandleServerErrorAsync(HttpContext context, string traceId, string errorMessage)
{
context.Response.StatusCode = 500;
var errorDto = new ErrorDto(new ValidationError("ServerError", errorMessage, new List<ValidationError>(), traceId));
await WriteResponseAsync(context, errorDto).ConfigureAwait(false);
}
private async Task WriteResponseAsync(HttpContext context, ErrorDto error)
{
var json = error.ToJson(_useStringEnumConverter ? Extensions.JsonSerializerOptions : Extensions.JsonSerializerOptionsWithoutEnumString);
await context.Response.WriteAsync(json).ConfigureAwait(false);
}
}
Steps to Reproduce
error :
{
"error": {
"code": "ServerError",
"message": "#Message: no decode delegate for this image format `' @ error/blob.c/CustomStreamToImage/828, #StackTrace:
at ImageMagick.NativeInstance.CheckException(IntPtr exception, IntPtr result) in /_/src/Magick.NET/Native/NativeInstance.cs:line 57\r\n
at ImageMagick.MagickImage.NativeMagickImage.ReadStream(IMagickSettings`1 settings, ReadWriteStreamDelegate reader, SeekStreamDelegate seeker, TellStreamDelegate teller, Void* data) in /_/src/Magick.NET/Native/MagickImage.cs:line 7100\r\n
at ImageMagick.MagickImage.NativeMagickImage.ReadStream(IMagickSettings`1 settings, ReadWriteStreamDelegate reader, SeekStreamDelegate seeker, TellStreamDelegate teller) in /_/src/Magick.NET/MagickImage.cs:line 7708\r\n
at ImageMagick.MagickImage.Read(Stream stream, IMagickReadSettings`1 readSettings, Boolean ping) in /_/src/Magick.NET/MagickImage.cs:line 7627\r\n
at ImageMagick.MagickImage.Read(Stream stream, IMagickReadSettings`1 readSettings) in /_/src/Magick.NET/MagickImage.cs:line 4907\r\n
at ImageMagick.MagickImage.Read(Stream stream, MagickFormat format) in /_/src/Magick.NET/MagickImage.cs:line 4898\r\n
at ImageMagick.MagickImage.Read(Stream stream) in /_/src/Magick.NET/MagickImage.cs:line 4889\r\n
at ImageMagick.MagickImage..ctor(Stream stream) in /_/src/Magick.NET/MagickImage.cs:line 171\r\n
at GoldenVitrin.Service.Services.CompressProviderService.CompressMedia(Stream stream, CancellationToken cancellationToken) in C:\\Users\\Lucifer\\source\\repos\\GoldenVitrinBackEnd\\src\\GoldenVitrin.Service\\Services\\CompressProviderService.cs:line 13\r\n
at GoldenVitrin.Service.Services.MinioService.UploadFileAsync(IFormFile file, String folderPath, CancellationToken cancellationToken) in C:\\Users\\Lucifer\\source\\repos\\GoldenVitrinBackEnd\\src\\GoldenVitrin.Service\\Services\\MinioService.cs:line 109\r\n
at GoldenVitrin.Service.Services.MinioService.<>c__DisplayClass10_0.<<UploadFilesAsync>b__0>d.MoveNext() in C:\\Users\\Lucifer\\source\\repos\\GoldenVitrinBackEnd\\src\\GoldenVitrin.Service\\Services\\MinioService.cs:line 149\r\n
--- End of stack trace from previous location ---\r\n
at System.Threading.Tasks.Parallel.<>c__53`1.<<ForEachAsync>b__53_0>d.MoveNext()\r\n
--- End of stack trace from previous location ---\r\n
at GoldenVitrin.Service.Services.MinioService.UploadFilesAsync(IList`1 files, String folderPath, CancellationToken cancellationToken) in C:\\Users\\Lucifer\\source\\repos\\GoldenVitrinBackEnd\\src\\GoldenVitrin.Service\\Services\\MinioService.cs:line 147\r\n
at GoldenVitrin.Service.Services.MediaService.SaveMultiMediaAsync(IList`1 inputFile, String webRootPath, String uploadFolder, CancellationToken cancellationToken) in C:\\Users\\Lucifer\\source\\repos\\GoldenVitrinBackEnd\\src\\GoldenVitrin.Service\\Services\\MediaService.cs:line 31\r\n
at GoldenVitrin.Service.CommandHandlers.Product.Vendor.CreateProductCommandHandlerV3.UploadedImage(UploadedImageProduct request) in C:\\Users\\Lucifer\\source\\repos\\GoldenVitrinBackEnd\\src\\GoldenVitrin.Service\\CommandHandlers\\Product\\Vendor\\CreateProductCommandHandlerV3.cs:line 132\r\n
at GoldenVitrin.Service.CommandHandlers.Product.Vendor.CreateProductCommandHandlerV3.HandleCore(CreateProductCommandV3Request request, CancellationToken cancellationToken) in C:\\Users\\Lucifer\\source\\repos\\GoldenVitrinBackEnd\\src\\GoldenVitrin.Service\\CommandHandlers\\Product\\Vendor\\CreateProductCommandHandlerV3.cs:line 50\r\n
at GoldenVitrin.Service.Behaviours.Vendor.CheckVariationBehaviour`2.HandleCore(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate`1 next) in C:\\Users\\Lucifer\\source\\repos\\GoldenVitrinBackEnd\\src\\GoldenVitrin.Service\\Behaviours\\Vendor\\CheckVariationBehaviour.cs:line 29\r\n
at SharedKernel.Common.Behavior.EventsBehaviour`2.HandleCore(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate`1 next) in C:\\Users\\Lucifer\\source\\repos\\GoldenVitrinBackEnd\\src\\BuildingBlocks\\GoldenVitrin.SharedKernel\\Common\\Behavior\\EventsBehaviour.cs:line 29\r\n
at SharedKernel.Common.Behavior.EventsBehaviour`2.HandleCore(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate`1 next) in C:\\Users\\Lucifer\\source\\repos\\GoldenVitrinBackEnd\\src\\BuildingBlocks\\GoldenVitrin.SharedKernel\\Common\\Behavior\\EventsBehaviour.cs:line 36\r\n
at SharedKernel.Common.Behavior.ValidationBehaviour`2.HandleCore(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate`1 next) in C:\\Users\\Lucifer\\source\\repos\\GoldenVitrinBackEnd\\src\\BuildingBlocks\\GoldenVitrin.SharedKernel\\Common\\Behavior\\ValidationBehaviour.cs:line 56\r\n
at GoldenVitrin.API.Controllers.Vendor.v2.VendorController.CreateProductAsync(ProductCommandV3 createCommand, CancellationToken cancellationToken) in C:\\Users\\Lucifer\\source\\repos\\GoldenVitrinBackEnd\\src\\GoldenVitrin.API\\Controllers\\Vendor\\v2\\VendorController.cs:line 154\r\n
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(ActionContext actionContext, IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)\r\n
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Logged|12_1(ControllerActionInvoker invoker)\r\n
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)\r\n
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)\r\n
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)\r\n
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)\r\n
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)\r\n
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)\r\n
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)\r\n
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)\r\n
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)\r\n
at Common.Core.Service.CustomExceptionMiddleware.InvokeAsync(HttpContext context) in C:\\Users\\Lucifer\\source\\repos\\GoldenVitrinBackEnd\\src\\BuildingBlocks\\GoldenVitrin.Common.Core\\Service\\CustomExceptionMiddleware.cs:line 45, #RequestBody: {discount:[0.1] vendorId:[1] taxAmount:[10000] isPublished:[true] price:[1200] name:[string] categoryId:[2147483647] availability:[All] description:[string]}",
"target": null,
"details": [],
"displayMode": "Toast",
"trackingNumber": null,
"traceIdentifier": "d95d9295-5652-44c2-92ea-a366d9d573f8"
}
}
Formatted by https://st.elmah.io
Magick.NET version
14.0.0
Environment (Operating system, version and so on)
windows 11
Description
I've been using a logging middleware to record errors along with user requests in the database. However, due to parallel image uploads and compression using Magick.NET, I encountered an error. What should I do? code :
upload parallel image :
`
`
compress method :
middleware :
Steps to Reproduce
error :
response_1727267569026.json