Search code examples
javascriptfile-uploadmultifile-uploaderscriptresource.axd

ASP.NET AJAX AjaxFileUpload: Uncaught error raising upload complete event and start new upload


I've added an AJAX multi file upload control to my web application. When I try to upload multiple files, only the 1st file is uploaded and the following error shows in the Chrome dev console:

Uncaught error raising upload complete event and start new upload

My control:

<cc1:AjaxFileUpload ID="multiUploader" ClientIDMode="Static" ClearFileListAfterUpload="false" OnClientUploadComplete="MultiUploadComplete" OnClientUploadCompleteAll="AllUploaded" OnUploadComplete="multiUploader_UploadComplete" MaximumNumberOfFiles="10" AllowedFileTypes="jpg,jpeg,png,gif" runat="server" />

JavaScript:

function MultiUploadComplete(sender, args) {
    var filesize = args.get_fileSize();
    var fileId = args.get_fileId();
    var status = document.getElementById('multiUploader_FileItemStatus_' + fileId);
    var container = document.getElementById('multiUploader_FileInfoContainer_' + fileId);
    var fileName = $(container).find('span.filename').html();

    if (filesize > 10240000) { 
        fileErrors += 1;
        if (status.innerText) {
            status.innerText = " (Error) " + $('#profilephotosize').attr('data-val').replace('{0}', fileName);
        }
        if (status.textContent) {
            status.textContent = " (Error) " + $('#profilephotosize').attr('data-val').replace('{0}', fileName);
        }
        container.style.color = 'Red';
    }
}

function AllUploaded() {
    if (fileErrors > 0) {
        alert($('#filesnotuploaded').attr('data-val').replace('{0}', fileErrors));
    }
    ShowData();
}

Backend code:

   Private Function ResizeAndSaveImage(ByVal maxWidth As Integer, ByVal maxHeight As Integer, ByVal path As String, ByVal img As Image) As Boolean
        'scale the image to maxWidth and maxHeight
        'save image
        Dim newWidth, newHeight As Integer
        Dim scaleFactor As Double
        Dim bResult As Boolean

        newWidth = img.Width
        newHeight = img.Height

        If img.Width > maxWidth Or img.Height > maxHeight Then
            If img.Width > maxWidth Then
                scaleFactor = maxWidth / img.Width
                newWidth = Math.Round(img.Width * scaleFactor, 0)
                newHeight = Math.Round(img.Height * scaleFactor, 0)
            End If
            If newHeight > maxHeight Then
                scaleFactor = maxHeight / newHeight
                newWidth = Math.Round(newWidth * scaleFactor, 0)
                newHeight = Math.Round(newHeight * scaleFactor, 0)
            End If
        End If


        'code below copied from: http://www.webcosmoforums.com/asp/321-create-high-quality-thumbnail-resize-image-dynamically-asp-net-c-code.html
        Try
            Dim bmp As New Bitmap(newWidth, newHeight)

            Dim gr As System.Drawing.Graphics = System.Drawing.Graphics.FromImage(bmp)
            gr.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality
            gr.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality
            gr.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High

            Dim rectDestination As New System.Drawing.Rectangle(0, 0, newWidth, newHeight)
            gr.DrawImage(img, rectDestination, 0, 0, img.Width, img.Height,
             GraphicsUnit.Pixel)

            bmp.Save(path)


            System.IO.File.WriteAllBytes(ChangeExtension(path, "webp"), imageFunctions.EncodeImageToWebP(bmp))


            bmp.Dispose()
            bResult = True

        Catch ex As Exception

        End Try

        Return bResult
    End Function


    Protected Sub afuPhoto_UploadedComplete(ByVal sender As Object, ByVal e As AjaxControlToolkit.AsyncFileUploadEventArgs)
        Dim afuPhoto As AjaxControlToolkit.AsyncFileUpload = CType(sender, AjaxControlToolkit.AsyncFileUpload)

        Dim pageId As Integer = 9

        If ConfigurationManager.AppSettings("isDevelopment") = "false" Then
            pageId = 1
        End If

        Dim allowedPhotos As Integer = ConfigurationManager.AppSettings("page_photos")

        Dim TA As New mysiteTableAdapters.sitephotoalbumsTableAdapter
        Dim totalPhotos As Integer = TA.CountPhotosForPageId(pageId)

        Session("pageid") = pageId
        'ReportError("afuPhoto_UploadedComplete.pageId", pageId.ToString)

        If totalPhotos >= allowedPhotos Then
            Exit Sub
        End If

        'code by mitsbits
        If afuPhoto.HasFile AndAlso e.State = AjaxControlToolkit.AsyncFileUploadState.Success Then

            'only if images are from same location, have same name and are uploaded at time calculated below a conflict could arise...chance minimal!
            Dim d As String = (DateTime.Now.Millisecond + DateTime.Now.Second).ToString

            If UploadFile(afuPhoto, pageId, d) = GlobalFunctions.ResultType.Success Then
                'if file upload successfull update database with photo
                TA.Insert(pageId, pageId.ToString + "_" + d + "_" + MakeValidImageName(afuPhoto.FileName), DateTime.Now)
            End If

        End If


    End Sub

    Protected Function UploadFile(ByVal FU As AjaxControlToolkit.AsyncFileUpload, ByVal locationId As Integer, ByVal curdate As String) As GlobalFunctions.ResultType
        Dim filename As String = MakeValidImageName(FU.PostedFile.FileName)
        Dim result As GlobalFunctions.ResultType
        Dim thumbgalleryPath, galleryPath, largethumbgalleryPath As String
        galleryPath = ConfigurationManager.AppSettings("page_photospath").ToString
        thumbgalleryPath = ConfigurationManager.AppSettings("page_thumbsphotospath").ToString
        largethumbgalleryPath = ConfigurationManager.AppSettings("page_largethumbsphotospath").ToString


        If FU.PostedFile.FileName IsNot Nothing And FU.PostedFile.ContentLength > 0 Then
            If FU.PostedFile.ContentLength <= 10240000 Then '10MB
                If Not filename.ToLower.Contains(".jpg") And Not filename.ToLower.Contains(".png") And Not filename.ToLower.Contains(".gif") And Not filename.ToLower.Contains(".jpeg") Then
                    Return ResultType.notallowed
                End If

                Dim imgOriginal As Image = System.Drawing.Image.FromStream(FU.PostedFile.InputStream)
                Dim imgOriginal1 As Image = imgOriginal 'this image is used as image is disposed

                'save large image and save thumb image
                If ResizeAndSaveImage(1200, 1200, Server.MapPath(galleryPath) + locationId.ToString + "_" + curdate + "_" + MakeValidImageName(filename), imgOriginal) And
                    ResizeAndSaveImage(450, 400, Server.MapPath(largethumbgalleryPath) + locationId.ToString + "_" + curdate + "_" + MakeValidImageName(filename), imgOriginal) And
                    ResizeAndSaveImage(75, 75, Server.MapPath(thumbgalleryPath) + locationId.ToString + "_" + curdate + "_" + MakeValidImageName(filename), imgOriginal) Then
                    result = ResultType.Success
                End If

                imgOriginal.Dispose()
            End If
        End If

        If Not result = GlobalFunctions.ResultType.Success Then
            'upload failed rollback
            GlobalFunctions.DeleteFile(Server.MapPath(galleryPath) + locationId.ToString + "_" + filename)
            GlobalFunctions.DeleteFile(Server.MapPath(largethumbgalleryPath) + locationId.ToString + "_" + filename)
            GlobalFunctions.DeleteFile(Server.MapPath(thumbgalleryPath) + locationId.ToString + "_" + filename)
        End If

        Return result
    End Function


    Protected Sub multiUploader_UploadComplete(sender As Object, e As AjaxControlToolkit.AjaxFileUploadEventArgs) 'Handles multiUploader.UploadComplete
        'check if the postback control was the linkbutton 'Opslaan', if it is, abort the upload function
        'Dim ctrlname As String = Page.Request.Params.Get("__EVENTTARGET") 'PS. this code does NOT work when checking for postbacks with BUTTON controls
        'If ctrlname.ToString <> "" AndAlso (ctrlname.Contains("btnSaveProfile") Or ctrlname.Contains("lbtnSetCoverPhoto")) Then 'save location button was clicked
        '    Exit Sub
        'End If
        'ReportError("multiUploader_UploadComplete")

        Dim pageId = 1

        Dim TAphotos As New mysiteTableAdapters.sitephotoalbumsTableAdapter
        Dim DTphotos As mysite.sitephotoalbumsDataTable = TAphotos.GetPagePhotos(pageId)


        Dim allowedPhotos As Integer = ConfigurationManager.AppSettings("page_photos")

        Dim totalPhotos As Integer = DTphotos.Rows.Count

        If totalPhotos >= allowedPhotos Then Exit Sub

        'code by mitsbits
        If e.State = AjaxControlToolkit.AjaxFileUploadState.Success Then 'multiUploader.afuPhoto.HasFile AndAlso

            'only if images are from same location, have same name and are uploaded at time calculated below a conflict could arise...chance minimal!
            'Dim d As String = (DateTime.Now.Millisecond + DateTime.Now.Second).ToString
            Dim d As String = DateTime.Now.Second.ToString + "_" + DateTime.Now.Millisecond.ToString
            Dim imageWidth, imageHeight As Integer



            If e.FileSize > 10240000 Then
                ScriptManager.RegisterClientScriptBlock(Me, Me.GetType(), "newfile",
    "alert('file too large');", True)

                Exit Sub
            End If

            'ReportError("e.filename", e.FileName.ToString)

            Dim result As GlobalFunctions.ResultType = UploadMultiFile(System.Drawing.Image.FromStream(e.GetStreamContents), e.FileName, pageId, d, imageWidth, imageHeight)
            If result = GlobalFunctions.ResultType.Success Then
                'if file upload successfull update database with photo
                Dim returnval As Integer

                'Dim clientIPAddress As String = Request.ServerVariables("REMOTE_ADDR").ToString

                returnval = TAphotos.Insert(pageId, pageId.ToString + "_" + d + "_" + MakeValidImageName(e.FileName), DateTime.Now)



            ElseIf result = ResultType.invalidtype Then


                '            '            ScriptManager.RegisterClientScriptBlock(Me, Me.GetType(), "newfile",
                '            '"window.parent.$find('" + multiUploader.ClientID + "').newFileName='invalidfiletype';", True)
                '            Try
                '                ScriptManager.RegisterClientScriptBlock(Me, Me.GetType(), "newfile",
                '"window.parent.$find('" + CType(dvSitePage.FindControl("multiUploader"), AjaxControlToolkit.AjaxFileUpload).ClientID + "').newFileName='invalidfiletype';", True)

                '            Catch ex As Exception

                '            End Try


            End If


        End If

    End Sub


    Protected Function UploadMultiFile(ByVal imgOriginal As Image, ByVal fileName As String, ByVal pageId As Integer, ByVal curdate As String, ByRef imageWidth As Integer, ByRef imageHeight As Integer) As GlobalFunctions.ResultType
        fileName = MakeValidImageName(fileName)
        Dim result As GlobalFunctions.ResultType
        Dim thumbgalleryPath, origgalleryPath, photopathMedium As String

        origgalleryPath = ConfigurationManager.AppSettings("page_photospath").ToString
        photopathMedium = ConfigurationManager.AppSettings("page_largethumbsphotospath").ToString
        thumbgalleryPath = ConfigurationManager.AppSettings("page_thumbsphotospath").ToString


        Dim imgOriginal1 As Image = imgOriginal 'this image is used as image is disposed

        'save large image and save thumb image
        If ResizeAndSaveImage(1200, 1200, Server.MapPath(origgalleryPath) + pageId.ToString + "_" + curdate + "_" + fileName, imgOriginal) And', imageWidth, imageHeight
            ResizeAndSaveImage(190, 190, Server.MapPath(photopathMedium) + pageId.ToString + "_" + curdate + "_" + fileName, imgOriginal) And
            ResizeAndSaveImage(75, 75, Server.MapPath(thumbgalleryPath) + pageId.ToString + "_" + curdate + "_" + fileName, imgOriginal) Then
            result = ResultType.Success
        End If


        imgOriginal.Dispose()



        If Not result = GlobalFunctions.ResultType.Success Then
            'upload failed rollback
            GlobalFunctions.DeleteFile(Server.MapPath(origgalleryPath) + pageId.ToString + "_" + fileName)
            GlobalFunctions.DeleteFile(Server.MapPath(thumbgalleryPath) + pageId.ToString + "_" + fileName)
            GlobalFunctions.DeleteFile(Server.MapPath(photopathMedium) + pageId.ToString + "_" + fileName)

            GlobalFunctions.DeleteFile(IO.Path.ChangeExtension(Server.MapPath(origgalleryPath) + pageId.ToString + "_" + fileName, "webp"))
            GlobalFunctions.DeleteFile(IO.Path.ChangeExtension(Server.MapPath(thumbgalleryPath) + pageId.ToString + "_" + fileName, "webp"))
            GlobalFunctions.DeleteFile(IO.Path.ChangeExtension(Server.MapPath(photopathMedium) + pageId.ToString + "_" + fileName, "webp"))
        Else
            'update successfull
        End If

        Return result
    End Function

I analyzed the Network tab:

And there are 3 requests, the last one fails somehow:

Request 1
Name: https://www.example.com/account/my-site?contextKey={DA8BEDC8-B952-4d5d-8CC2-59FE922E2923}&controlID=multiUploader&start=1&queue=2&
Status: 200
Initiator: https://www.example.com/ScriptResource.axd?d=-9YDjdHizPInlZIfdnhPn9wQrtV-icwCIGM6rMTgL1xcc9eo0V8JJ8oN6GiCmQReZbL-gv3nU-BhwRv3l8r5gubGD2yQ03ZVzdvO2Ko-nvG4Lmxrd4NQGjsi7m-ARIjq0&t=7d5986a

Request 2
Name: https://www.example.com/AjaxFileUploadHandler.axd?contextKey={DA8BEDC8-B952-4d5d-8CC2-59FE922E2923}&controlID=multiUploader&fileId=AF96CF7B-B0EF-A47F-36E6-4E75CABB28D8&fileName=tt-ftp-settings.jpg&chunked=false&firstChunk=true
Status: 200
Initiator: https://www.example.com/ScriptResource.axd?d=-9YDjdHizPInlZIfdnhPn9wQrtV-icwCIGM6rMTgL1xcc9eo0V8JJ8oN6GiCmQReZbL-gv3nU-BhwRv3l8r5gubGD2yQ03ZVzdvO2Ko-nvG4Lmxrd4NQGjsi7m-ARIjq0&t=7d5986a

Request 3
Name: https://www.example.com/account/my-site?contextKey={DA8BEDC8-B952-4d5d-8CC2-59FE922E2923}&controlID=multiUploader&done=1&guid=AF96CF7B-B0EF-A47F-36E6-4E75CABB28D8&
Status: 500
Initiator: https://www.example.com/ScriptResource.axd?d=-9YDjdHizPInlZIfdnhPn9wQrtV-icwCIGM6rMTgL1xcc9eo0V8JJ8oN6GiCmQReZbL-gv3nU-BhwRv3l8r5gubGD2yQ03ZVzdvO2Ko-nvG4Lmxrd4NQGjsi7m-ARIjq0&t=7d5986a

Also checked here, but that also relates to AjaxFileUploadHandler.axd and not to ScriptResource.axd. https://github.com/DevExpress/AjaxControlToolkit/issues/82

I added the answer from sridharnethato for AjaxFileUploadHandler.axd to my web.config: AjaxControlToolkit: error raising upload complete event and start new upload Also tried the other answers in this thread.

My current issue relates to ScriptResource.axd, but I don't know why request 1 is successful, but request 3 is not, even though they look practically the same.

What can I do to resolve the above error?

UPDATE 1

Started testing answer from @Greg, but I get error on function SaveImage: {Message: "An error has occurred.", ExceptionMessage: "A generic error occurred in GDI+.",…}

UPDATE 2

Found the issue, apparently MapPath is inserting the route into the filepath? Any way to prevent this? This is my definition:

 <Route("api2/UploadFile/")>
    Function UploadFile() As System.Web.Http.IHttpActionResult

        Dim httpRequest = HttpContext.Current.Request

        If ValidateImage(httpRequest.Files) Then
            Dim file = httpRequest.Files(0)

            Dim fileName = Path.GetFileName(file.FileName)


            Dim galleryPath As String = ConfigurationManager.AppSettings("page_photospath").ToString
                    

            Dim filePath As String = HttpContext.Current.Server.MapPath(galleryPath) + fileName 
                
            ===> HERE filePath IS: "C:\inetpub\example\api2\images\photos\Catan.jpg" where it should be: "C:\inetpub\example\images\photos\Catan.jpg"
            Apparently `MapPath` is inserting the route into the path? Any way to prevent this?
            

            SaveImage(file, filePath)

            Return Ok()
        Else
            Return BadRequest()
        End If
    End Function

Solution

  • A 500 error indicates that a request was made to a web server and it could not handle it. It will be tricky to find the root cause, as the issue could be front-end, back-end, environment (web.config / app pool settings) or an issue with DevExpress.

    Therefore you should run the code in isolate. For example: create a blank aspx page (no Master Page), which contains the AjaxFileUpload and has empty function/methods. If this works, then add the bare minimum code to save an image (to a temp folder). If this works, then copy code across bit-by-bit until you can create the error.

    Alternatively add some logging to your back-end code. It looks like you have a try/catch that hides exceptions. If there's an error then the variables results is returned as null. It look like MultiUploadComplete() doesn't checks for success, but merely the size of the file?

    Alternatively re-write the AjaxFileUpload. It's not well supported and modern JavaScript, HTML and web browsers means that it's a lot easier to implement.

    Take a look at https://www.smashingmagazine.com/2018/01/drag-drop-file-uploader-vanilla-js/. This article shows how to create a mutli-file drag & drop uploader using pure JavaScript, HTML5. You'll need to create an end point, preferably using a WebApi or generic handler (if using an older version of asp.net)

    As a simple proof of concept. I've put together an aspx page, which post images to an api. It sends the files individually at once for performance (but you can do it all as one request). The main thing to point out is that theres no third part libraries and you have flexibility to change the look and feel of you file uploader.

    ASPX code

    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="default.aspx.cs" Inherits="WebForms._default" %>
    
    <!DOCTYPE html>
    <html lang="en">
        <head runat="server">
            <title></title>
        </head>
        <body>
            <form id="form1" runat="server">
                <div id="drop-area">
                <p>Drop files here</p>
                <input type="file" id="fileElem" multiple accept="image/*" onchange="handleFiles(this.files)">
                <label class="button" for="fileElem">Select files</label>
                <progress id="progress-bar" max=100 value=0></progress>
                <div id="gallery" /></div>
                <input id="btnUpload" type="submit" value="Upload" />    
            </form> 
            <script type="text/javascript">     
                let btnUpload = document.getElementById("btnUpload")
                btnUpload.addEventListener('click', uploadFiles, false)
    
                function uploadFiles(event) {
                    event.preventDefault();
                    // TODO - validate file size, extension & amount
    
                    files = [...fileElem.files]
                    // Submit each file separately.
                    files.forEach(uploadFile)
                }
    
                // This all copy & paste
                // ************************ Drag and drop ***************** //
                let dropArea = document.getElementById("drop-area")
    
                    // Prevent default drag behaviors
                    ;['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
                        dropArea.addEventListener(eventName, preventDefaults, false)
                        document.body.addEventListener(eventName, preventDefaults, false)
                    })
    
                    // Highlight drop area when item is dragged over it
                    ;['dragenter', 'dragover'].forEach(eventName => {
                        dropArea.addEventListener(eventName, highlight, false)
                    })
    
                    ;['dragleave', 'drop'].forEach(eventName => {
                        dropArea.addEventListener(eventName, unhighlight, false)
                    })
    
                // Handle dropped files
                dropArea.addEventListener('drop', handleDrop, false)
    
                function preventDefaults(e) {
                    e.preventDefault()
                    e.stopPropagation()
                }
    
                function highlight(e) {
                    dropArea.classList.add('highlight')
                }
    
                function unhighlight(e) {
                    dropArea.classList.remove('active')
                }
    
                function handleDrop(e) {
                    var dt = e.dataTransfer
                    var files = dt.files
    
                    handleFiles(files)
                }
    
                let uploadProgress = []
                let progressBar = document.getElementById('progress-bar')
    
                function initializeProgress(numFiles) {
                    progressBar.value = 0
                    uploadProgress = []
    
                    for (let i = numFiles; i > 0; i--) {
                        uploadProgress.push(0)
                    }
                }
    
                function updateProgress(fileNumber, percent) {
                    uploadProgress[fileNumber] = percent
                    let total = uploadProgress.reduce((tot, curr) => tot + curr, 0) / uploadProgress.length
                    //console.log('update', fileNumber, percent, total)
                    progressBar.value = total
                    return total === 100; 
                }
    
                function handleFiles(files) {
                    files = [...files]
                    initializeProgress(files.length)
                    //files.forEach(uploadFile)
                    files.forEach(previewFile)
                }
    
                function previewFile(file) {
                    let reader = new FileReader()
                    reader.readAsDataURL(file)
                    reader.onloadend = function () {
                        let img = document.createElement('img')
                        img.src = reader.result
                        document.getElementById('gallery').appendChild(img)
                    }
                }
    
                function uploadFile(file, i) {
                    var url = '/api/UploadFile' // TODO: change end point
                    var xhr = new XMLHttpRequest()
                    var formData = new FormData()
                    xhr.open('POST', url, true)
                    xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest')
    
                    // Update progress (can be used to show progress indicator)
                    xhr.upload.addEventListener("progress", function (e) {
                        updateProgress(i, (e.loaded * 100.0 / e.total) || 100)
                    })
    
                    xhr.addEventListener('readystatechange', function (e) {
                        if (xhr.readyState == 4 && xhr.status == 200) {
                            if (updateProgress(i, 100)) {
                                alert('Complete') // TODO
                            }
                        }
                        else if (xhr.readyState == 4 && xhr.status != 200) {
                            alert('error') // TODO
                        }
                    })
    
                    formData.append('file', file)
                    xhr.send(formData)
                }
            </script>
    
            <style type="text/css">
            body {
                font-family: sans-serif;
            }
            a {
                color: #369;
            }
            .note {
                width: 500px;
                margin: 50px auto;
                font-size: 1.1em;
                color: #333;
                text-align: justify;
            }
            #drop-area {
                border: 2px dashed #ccc;
                border-radius: 20px;
                width: 480px;
                margin: 50px auto;
                padding: 20px;
            }
            #drop-area.highlight {
                border-color: purple;
            }
            p {
                margin-top: 0;
            }
            .my-form {
                margin-bottom: 10px;
            }
            #gallery {
                margin-top: 10px;
            }
            #gallery img {
                width: 150px;
                margin-bottom: 10px;
                margin-right: 10px;
                vertical-align: middle;
            }
            .button {
                display: inline-block;
                padding: 10px;
                background: #ccc;
                cursor: pointer;
                border-radius: 5px;
                border: 1px solid #ccc;
            }
            .button:hover {
                background: #ddd;
            }
            #fileElem {
                display: none;
            }
        </style>
        </body>
    </html>
    

    Web API 2.1 (I've chosen C# as I've touched VB in years)

    using System;
    using System.Drawing;
    using System.IO;
    using System.Linq;
    using System.Web;
    using System.Web.Http;
    
    namespace WebForms.Controllers
    {
        public class UploadFilesController : ApiController
        {
            [Route("api/UploadFile/")]
            public IHttpActionResult UploadFile()
            {
                var httpRequest = HttpContext.Current.Request;
               
                if (ValidateImage(httpRequest.Files))
                {
                    var file = httpRequest.Files[0];
                    var fileName = Path.GetFileName(file.FileName);
                    var filePath = HttpContext.Current.Server.MapPath($"~/{fileName}");
                   
                    SaveImage(file, filePath);
                    return Ok();
                }
                else
                {
                    return BadRequest();
                }            
            }
    
            private void SaveImage(HttpPostedFile file, string filePath)
            {
                using (var sourceimage = Image.FromStream(file.InputStream))
                {
                    sourceimage.Save(filePath);
                }
            }
                
            private bool ValidateImage(HttpFileCollection files)
            {
                return IsSingleFile(files)
                && IsValidFileType(files[0].FileName, files[0].ContentType)
                && IsValidSize(files[0].ContentLength);
            }
    
            public bool IsSingleFile(HttpFileCollection files)
            {
                return files.Count == 1;
            }
    
            public bool IsValidFileType(string fileName, string contentType)
            {
                string[] extensions = { "jpg", "jpeg", "gif", "bmp", "png" };
                return extensions.Any(extension => $"image/{extension}".Equals(contentType, StringComparison.OrdinalIgnoreCase))
                && extensions.Any(extension => $".{extension}".Equals(Path.GetExtension(fileName.ToLower()), StringComparison.OrdinalIgnoreCase));
            }
    
            public bool IsValidSize(int contentLength)
            {
                return contentLength < 2100000; // 2MBs
            }        
        }
    }