umbraco / Umbraco.Forms.Issues

Public issue tracker for Umbraco Forms
29 stars 0 forks source link

Send multiple upload files to other system fails #1190

Open Ramya-Devendiran opened 3 months ago

Ramya-Devendiran commented 3 months ago

I created a form with short answer, long answer and file upload (multiple) fields. I have to send the response data to another system.

I have created a custom workflow to send the data as MultipartFormDataContent to an endpoint using httpclient. The WORKFLOW is hit when attaching two files and the notification is sent to external system.

But on attaching more than two files, the webpage goes DOWN. The workflow is not hit and not executed.

I think the file path length might be an issue. C:\Users\ramya\projects\abc\wwwroot\media\forms\upload\form_b821397b-4b5e-4cd3-8353-440c1970196d\1c9e97ce-a564-4040-96c5-6c586bd01b1a/251_ne-scaled_145839f169.jpg C:\Users\ramya\projects\abc\wwwroot\media\forms\upload\form_b821397b-4b5e-4cd3-8353-440c1970196d\1c9e97ce-a564-4040-96c5-6c586bd01b1a/251_ne-scaled_145839f170.jpg C:\Users\ramya\projects\abc\wwwroot\media\forms\upload\form_b821397b-4b5e-4cd3-8353-440c1970196d\1c9e97ce-a564-4040-96c5-6c586bd01b1a/251_ne-scaled_145839f171.jpg

Reproduction

Steps to reproduce: Create a form with short answer, long answer and file upload (multiple) fields. Add a custom workflow to send a notification to an external system.

Enter all form fields and attach 4 files and submit the form.

Expected result: Custom workflow should be called and processed.

Actual result; The webpage went down with message webpage might be temporarily down or it may have moved permanently to a new web address. Umbraco 12.3.6 Umbraco Forms: 12.2.2

AndyButland commented 3 months ago

I think to investigate this please can you provide a minimal version of your custom workflow that reproduces the problem? Not to be sending files to your actual external system of course, but if you can replicate with saving files to disk or something like that, we'll have something to reproduce locally to investigate.

Ramya-Devendiran commented 3 months ago

CustomWorkflow.txt The workflow is hit on attaching couple of files. The issue is when more than 2 files are uploaded. How do I override the path of the files storage location. C:\Users\ramya\projects\abc\wwwroot\media\forms\upload\form_{guid1}{guid2}/251_ne-scaled_145839f171.jpg to C:\Users\ramya\projects\abc\wwwroot\media\abcdefgh\251_ne-scaled_145839f171.jpg. I didnt really understand "replicate with saving files to disk" and how to perform it.

AndyButland commented 3 months ago

Hi @Ramya-Devendiran - I couldn't replicate the issue from your provided workflow I'm afraid, it seems to run through without any errors or locking for me, I did find an issue with how you were finding the paths to the file, as you had a mix of \ and / path separators. But having fixed that again I didn't see any problems.

You are having to convert an asynchronous result here into a synchronous one, which, when necessary, I generally use .GetAwaiter().GetResult() over .Result. As an aside, isn't necessary in Forms 13 as we made workflow execution asynchronous there.

Here's your workflow back with these small updates, but, as I say, I couldn't replicate the issue you described either with your original version or my slightly amended one.

using Umbraco.Forms.Core;
using Umbraco.Forms.Core.Enums;
using Umbraco.Forms.Core.Persistence.Dtos;

namespace Umbraco.Forms.TestSite.Business.Workflows
{
    public class TicketWorkflow : WorkflowType
    {
        private readonly ILogger<TicketWorkflow> _logger;
        private readonly IWebHostEnvironment _webHostEnvironment;

        public TicketWorkflow(
            IWebHostEnvironment webHostEnvironment,
            ILogger<TicketWorkflow> logger)
        {
            _logger = logger;
            _webHostEnvironment = webHostEnvironment;
            this.Id = new Guid("ccbeb0d5-adaa-4729-8b4c-4bb439dc0202");
            this.Name = "TicketWorkflow";
            this.Description = "This workflow is to send response to Ticket system";
            this.Icon = "icon-chat-active";
            this.Group = "Services";
        }

        public override WorkflowExecutionStatus Execute(WorkflowExecutionContext context)
        {
            _logger.LogInformation("Custom workflow started");
            return ProcessAsync(context).GetAwaiter().GetResult();
        }

        private async Task<WorkflowExecutionStatus> ProcessAsync(WorkflowExecutionContext context)
        {
            var formData = new MultipartFormDataContent();
            foreach (RecordField rf in context.Record.RecordFields.Values)
            {
                List<object> vals = rf.Values;
                if (rf.Field != null && rf.Field.AllowedUploadTypes != null && rf.Field.AllowedUploadTypes.Count() > 0)
                {
                    foreach (object fileName in vals)
                    {
                        var filePath = _webHostEnvironment.MapPathWebRoot(fileName.ToString()!);
                        var fileContent = new ByteArrayContent(await File.ReadAllBytesAsync(filePath));

                        if (File.Exists(filePath))
                        {
                            _logger.LogInformation($"Sending file uploaded to: {filePath}");
                            formData.Add(fileContent, rf.Alias, Path.GetFileName(filePath));
                        }
                        else
                        {
                            _logger.LogWarning($"Could not find file at: {filePath}");
                        }
                    }
                }
                else
                {
                    formData.Add(new StringContent(rf.ValuesAsString(false)), rf.Alias);
                }
            }

            //call httpClient to send the formdata to external system;

            return WorkflowExecutionStatus.Completed;
        }

        public override List<Exception> ValidateSettings()
        {
            return new List<Exception>();
        }
    }
}
Ramya-Devendiran commented 3 months ago

The issue is not with the functional code in the workflow. Even if it has only a log statement, It is not working. public override WorkflowExecutionStatus Execute(WorkflowExecutionContext context) { _logger.LogInformation("Custom workflow started"); return WorkflowExecutionStatus.Completed; } Upload file field settings: Predefined allowed file types: png, pdf and jpg. and User defined allowed file types: JPEG is added. I am able to add multiple files in pdf, jpg and jpeg with even huge size (2.5 mb) files. I think the issue occurs uploading png files.

Can you please retest adding a png file?

Thanks for the tip about usage of GetAwaiter().GetResult() instead .Result.

AndyButland commented 3 months ago

I've tested with png and jpg files earlier but couldn't see any issue I'm afraid.

Ramya-Devendiran commented 3 months ago

Thanks for the quick response. I am testing in local. I shall test it in stage environment.

Ramya-Devendiran commented 3 months ago

I receive the same issue. I removed the custom workflow. Upload of png is not working still.

error "An unhandled exception has occurred while executing the request." . Can you figure anything out the error message. Microsoft.AspNetCore.Connections.ConnectionResetException: The client has disconnected ---> System.Runtime.InteropServices.COMException (0x800704CD): An operation was attempted on a nonexistent network connection. (0x800704CD) --- End of inner exception stack trace --- at Microsoft.AspNetCore.Server.IIS.Core.IO.AsyncIOOperation.GetResult(Int16 token) at Microsoft.AspNetCore.Server.IIS.Core.IISHttpContext.ReadBody() at System.IO.Pipelines.Pipe.GetReadResult(ReadResult& result) at System.IO.Pipelines.Pipe.ReadAsync(CancellationToken token) at Microsoft.AspNetCore.Server.IIS.Core.IISHttpContext.ReadAsync(Memory1 memory, CancellationToken cancellationToken) at Microsoft.AspNetCore.Server.IIS.Core.HttpRequestStream.ReadAsyncInternal(Memory1 buffer, CancellationToken cancellationToken) at Microsoft.AspNetCore.WebUtilities.BufferedReadStream.EnsureBufferedAsync(Int32 minCount, CancellationToken cancellationToken) at Microsoft.AspNetCore.WebUtilities.MultipartReaderStream.ReadAsync(Memory1 buffer, CancellationToken cancellationToken) at Microsoft.AspNetCore.WebUtilities.StreamHelperExtensions.DrainAsync(Stream stream, ArrayPool1 bytePool, Nullable`1 limit, CancellationToken cancellationToken) at Microsoft.AspNetCore.WebUtilities.MultipartReader.ReadNextSectionAsync(CancellationToken cancellationToken) at Microsoft.AspNetCore.Http.Features.FormFeature.InnerReadFormAsync(CancellationToken cancellationToken) at Microsoft.AspNetCore.Http.Features.FormFeature.ReadForm() at Umbraco.Cms.Web.Common.AspNetCore.AspNetCoreRequestAccessor.GetFormValue(String name) at Umbraco.Cms.Web.Common.AspNetCore.AspNetCoreRequestAccessor.GetRequestValue(String name) at Umbraco.Cms.Core.Routing.ContentFinderByPageIdQuery.TryFindContent(IPublishedRequestBuilder frequest) at Umbraco.Cms.Core.Routing.PublishedRouter.FindPublishedContent(IPublishedRequestBuilder request) at Umbraco.Cms.Core.Routing.PublishedRouter.RouteRequestInternalAsync(IPublishedRequestBuilder builder, Boolean skipContentFinders) at Umbraco.Cms.Core.Routing.PublishedRouter.RouteRequestAsync(IPublishedRequestBuilder builder, RouteRequestOptions options) at Umbraco.Cms.Web.Website.Routing.UmbracoRouteValueTransformer.RouteRequestAsync(IUmbracoContext umbracoContext) at Umbraco.Cms.Web.Website.Routing.UmbracoRouteValueTransformer.TransformAsync(HttpContext httpContext, RouteValueDictionary values) at Microsoft.AspNetCore.Mvc.Routing.DynamicControllerEndpointMatcherPolicy.ApplyAsync(HttpContext httpContext, CandidateSet candidates) at Microsoft.AspNetCore.Routing.Matching.DfaMatcher.SelectEndpointWithPoliciesAsync(HttpContext httpContext, IEndpointSelectorPolicy[] policies, CandidateSet candidateSet) at Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware.gAwaitMatch|8_1(EndpointRoutingMiddleware middleware, HttpContext httpContext, Task matchTask) at StackExchange.Profiling.MiniProfilerMiddleware.Invoke(HttpContext context) in C:\projects\dotnet\src\MiniProfiler.AspNetCore\MiniProfilerMiddleware.cs:line 114 at Umbraco.Cms.Web.Common.Middleware.UmbracoRequestMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) at Umbraco.Cms.Web.Common.Middleware.UmbracoRequestMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.<>c__DisplayClass6_1.<b1>d.MoveNext() --- End of stack trace from previous location --- at Umbraco.Cms.Web.Common.Middleware.PreviewAuthenticationMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.<>cDisplayClass6_1.<b1>d.MoveNext() --- End of stack trace from previous location --- at Umbraco.Cms.Web.Common.Middleware.UmbracoRequestLoggingMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.<>cDisplayClass6_1.<b1>d.MoveNext() --- End of stack trace from previous location --- at SixLabors.ImageSharp.Web.Middleware.ImageSharpMiddleware.Invoke(HttpContext httpContext, Boolean retry) at Umbraco.Commerce.Cms.Web.Mvc.UmbracoCommerceRequestBufferingMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.<>cDisplayClass6_1.<b1>d.MoveNext() --- End of stack trace from previous location --- at Umbraco.Forms.Web.HttpModules.ProtectFormUploadRequestsMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.<>cDisplayClass6_1.<b1>d.MoveNext() --- End of stack trace from previous location --- at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)

Ramya-Devendiran commented 3 months ago

I deactivated the custom workflow. Upload png file is not working in stage too. Is there any way to log forms error? The form has recaptcha V3 hidden mandatory field. I forgot to mention this in the reproduct steps.

Is it possible for you to share the upload field settings and the form pageUrl?

Please help to find the root cause of the bug.

AndyButland commented 3 months ago

Forms errors are logged - you should see them via the Umbraco log viewer. However it doesn't look like Forms is throwing an error in your case. There's clearly something up - An operation was attempted on a nonexistent network connection - is a weird error to get though, and not one I've heard of in this context before. Given you only see it in staging and not locally, that suggests something environment specific is causing it. I think you'll have to break it down to as simple a case as you can, and perhaps trying looking at file uploads outside of Forms.

Ramya-Devendiran commented 3 months ago

The error occurs in localhost too. I wanted to test the changes in stage to see if there is any environment issue. The solution is a multi language site.

I will try to create custom field type File Upload or create my own form.

Thanks for your support. I will update you if custom field type works.