public ActionResult Upload(UploadFormModel model)
        {
            if (!ModelState.IsValid)
            {
                //You would actually return the errors allowing the user to correct them
                return(Json(new { Confirm = false, isJsonErrorList = true, message = $"{model.Firstname} Form Fields Not Valid" }, JsonRequestBehavior.AllowGet));
            }
            var files = Request.Files;
            HttpPostedFileBase image = files[0];

            if (image != null && image.ContentLength > 0)
            {
                //Here you would do whatever validation you require

                bool isImageValid = IsFileTypeValid(image);

                if (isImageValid)
                {
                    //Either send as attachemnt or save to disk
                    //Then return message to user
                    return(Json(new { Confirm = true, isJsonErrorList = false, message = $"{model.Firstname} Image Uploaded" }, JsonRequestBehavior.AllowGet));
                }
                return(Json(new { Confirm = true, isJsonErrorList = false, message = $"{model.Firstname} Image Upload failed" }, JsonRequestBehavior.AllowGet));
            }

            return(Json(new { Confirm = false, isJsonErrorList = true, message = $"{model.Firstname} Image Upload failed" }, JsonRequestBehavior.AllowGet));
        }
        public async Task <IActionResult> Index(UploadFormModel model, CancellationToken cancellationToken)
        {
            _logger.LogInformation("{UserName} called file upload action", User.Identity.Name);

            if (!ModelState.IsValid)
            {
                TempData["Alert.Type"]    = "danger";
                TempData["Alert.Message"] = "Failed to upload files. Please correct the errors and try again.";
                return(View("~/Views/Home/Index.cshtml"));
            }

            var traceId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier;

            using var scope = _logger.BeginScope("{UserName} {TraceID}", User.Identity.Name, traceId);

            string email = User.GetUserGraphEmail();

            if (string.IsNullOrEmpty(email))
            {
                _logger.LogError("Could not get associated email address for user {UserName}", User.Identity.Name);

                TempData["Alert.Type"]    = "danger";
                TempData["Alert.Message"] = "Could not find your email address.";
                return(View("~/Views/Home/Index.cshtml"));
            }

            // Uploaded files
            List <IFormFile> files = model.File;

            foreach (IFormFile file in files)
            {
                cancellationToken.ThrowIfCancellationRequested();

                string untrustedFileName = Path.GetFileName(file.FileName);
                string encodedFileName   = HttpUtility.HtmlEncode(untrustedFileName);

                _logger.LogInformation("Upload file name: {FileName}, size: {FileSize}", untrustedFileName, file.Length);

                // Check the file length
                if (file.Length == 0)
                {
                    _logger.LogWarning("File {FileName} is empty.", untrustedFileName);

                    ModelState.AddModelError(file.Name, $"File {encodedFileName} is empty.");
                    TempData["Alert.Type"]    = "danger";
                    TempData["Alert.Message"] = "Failed to upload files. Please correct the errors and try again.";
                    return(View("~/Views/Home/Index.cshtml"));
                }

                // Check the content type and file extension
                if (!IsValidImage(file))
                {
                    _logger.LogWarning("File {FileName} has unsupported file extension.", untrustedFileName);

                    ModelState.AddModelError(file.Name,
                                             $"File {encodedFileName} has unsupported file extension. " +
                                             "Support file extensions are .jpg, .jpeg, .png, .gif, .bmp, .heic");
                    TempData["Alert.Type"]    = "danger";
                    TempData["Alert.Message"] = "Failed to upload files. Please correct the errors and try again.";
                    return(View("~/Views/Home/Index.cshtml"));
                }

                try
                {
                    byte[] data;
                    using (var ms = new MemoryStream())
                    {
                        await file.CopyToAsync(ms);

                        data = ms.ToArray();
                    }

                    // Convert HEIC files to JPEG since browsers are unable to display them
                    var isHeic = false;
                    if (Path.GetExtension(untrustedFileName).ToLowerInvariant() == ".heic")
                    {
                        isHeic = true;
                        _logger.LogInformation("Converting {FileName} to JPEG", untrustedFileName);
                        data = ConvertToJpeg(data);
                        untrustedFileName = Path.GetFileNameWithoutExtension(untrustedFileName) + ".jpg";
                    }

                    //upload to a separate container to retrieve image URL
                    //use unique id together with file name to avoid duplication
                    string id           = GenerateId();
                    string blobFileName = id + "_" + untrustedFileName;
                    string contentType  = isHeic ? "image/jpeg" : file.ContentType;
                    string imageURL;
                    using (var uploadImage = new MemoryStream(data, false))
                    {
                        imageURL = await ImageUploadToBlob(blobFileName, uploadImage, contentType, _appSettings);
                    }

                    //extract image metadata
                    IReadOnlyList <MetadataExtractor.Directory> directories;
                    using (var extractMetadataImage = new MemoryStream(data, false))
                    {
                        directories = ImageMetadataReader.ReadMetadata(extractMetadataImage);
                    }

                    // Check image size as Cognitive Services can only accept images less than 4MB in size

                    _logger.LogInformation("Starting FitImageForAnalysis");
                    var    watch  = Stopwatch.StartNew();
                    byte[] fitted = FitImageForAnalysis(data);
                    watch.Stop();
                    _logger.LogInformation("Finished FitImageForAnalysis after {Elapsed} ms", watch.ElapsedMilliseconds);

                    ImageAnalysis computerVisionResult;
                    string        thumbnailFileName = Path.GetFileNameWithoutExtension(blobFileName) + "_thumb.jpg";
                    string        thumbnailURL;
                    if (fitted != null)
                    {
                        // Get tags and content-aware thumbnail from Cognitive Services
                        using var thumbnailStream = new MemoryStream(fitted, false);
                        using var imageStream     = new MemoryStream(fitted, false);

                        thumbnailURL = await GenerateThumbnailAsync(thumbnailFileName, thumbnailStream, _appSettings);

                        computerVisionResult = await CallCSComputerVisionAsync(imageStream, _appSettings);
                    }
                    else
                    {
                        // Unable to fit within size limit
                        // Skip calling Cognitive Services and return empty analysis results
                        using var thumbnailStream = new MemoryStream(data, false);
                        using var imageStream     = new MemoryStream(data, false);

                        thumbnailURL = await GenerateThumbnailMagickAsync(thumbnailFileName, thumbnailStream, _appSettings);

                        computerVisionResult = new ImageAnalysis()
                        {
                            Tags        = Array.Empty <ImageTag>(),
                            Description = new ImageDescriptionDetails(null, Array.Empty <ImageCaption>())
                        };
                    }

                    //create json for indexing
                    ImageEntity json = new ImageEntity();
                    json.PartitionKey = TransferPartitionKey;
                    json.RowKey       = id;
                    json.Id           = id;
                    json.Name         = untrustedFileName;
                    json.DateTaken    = GetTimestamp(directories, _appSettings.UploadTimeZone);
                    json.Location     = JsonConvert.SerializeObject(GetCoordinate(directories));
                    json.Tag          = GenerateTags(computerVisionResult);
                    json.Caption      = GenerateCaption(computerVisionResult);
                    json.Author       = email;
                    json.UploadDate   = TruncateMilliseconds(DateTime.UtcNow);
                    json.FileURL      = imageURL;
                    json.ThumbnailURL = thumbnailURL;
                    json.Project      = model.Project;
                    json.LocationName = model.LocationText;
                    json.Copyright    = model.Copyright;

                    await IndexUploadToTable(json, _appSettings);
                }
                catch (Exception e)
                {
                    _logger.LogError(e, "File {FileName} failed to upload.", untrustedFileName);

                    ModelState.AddModelError(file.Name, $"File {encodedFileName} could not be uploaded.");
                    TempData["Alert.Type"]    = "danger";
                    TempData["Alert.Message"] = "Failed to upload files. Please correct the errors and try again.";
                    return(View("~/Views/Home/Index.cshtml"));
                }
            }
            ModelState.Clear();
            TempData["Alert.Type"]    = "success";
            TempData["Alert.Message"] = "Your items have been uploaded successfully, and will be copied to intranet in 10 minutes.";
            return(View("~/Views/Home/Index.cshtml"));
        }