public async Task <ActionResult <UploadFileResponse> > StartFileUpload(
            [Required][FromBody] UploadFileRequestForm request)
        {
            if (!CheckNewItemName(request.Name, out var badRequest))
            {
                return(badRequest !);
            }

            if (!remoteStorage.Configured)
            {
                throw new HttpResponseException()
                      {
                          Status = StatusCodes.Status500InternalServerError,
                          Value  = "Remote storage is not configured on the server",
                      };
            }

            // Disallow extensions with uppercase letters
            if (PathParser.IsExtensionUppercase(request.Name))
            {
                return(BadRequest("File extension can't contain uppercase characters"));
            }

            // TODO: maybe in the future we'll want to allow anonymous uploads to certain folders
            var user = HttpContext.AuthenticatedUserOrThrow();

            // Check write access
            StorageItem?parentFolder = null;

            if (request.ParentFolder != null)
            {
                parentFolder = await database.StorageItems.FirstOrDefaultAsync(i =>
                                                                               i.Ftype == FileType.Folder && i.Id == request.ParentFolder.Value);

                if (parentFolder == null)
                {
                    return(NotFound("Parent folder doesn't exist"));
                }
            }

            // Check if the item already exists (a new version is being uploaded)
            var parentId     = parentFolder?.Id;
            var existingItem =
                await database.StorageItems.FirstOrDefaultAsync(i => i.ParentId == parentId && i.Name == request.Name);

            if (existingItem != null)
            {
                // New version of an existing item. User needs at least read access to the folder and
                // Root folder is publicly readable so that doesn't need to be checked here
                if (parentFolder != null)
                {
                    if (!parentFolder.IsReadableBy(user))
                    {
                        return(this.WorkingForbid("You don't have read access to the folder"));
                    }
                }

                // Disallow file uploads to a folder item
                if (existingItem.Ftype != FileType.File)
                {
                    return(BadRequest("Can't upload a new file version to an item that is not a file"));
                }
            }
            else
            {
                // Write access required to make a new item
                if (parentFolder == null)
                {
                    if (!user.HasAccessLevel(UserAccessLevel.Admin))
                    {
                        return(this.WorkingForbid("Only admins can write to root folder"));
                    }
                }
                else
                {
                    if (!parentFolder.IsWritableBy(user))
                    {
                        return(this.WorkingForbid("You don't have write access to the folder"));
                    }
                }
            }

            if (existingItem == null)
            {
                existingItem = new StorageItem()
                {
                    Name            = request.Name,
                    Ftype           = FileType.File,
                    ReadAccess      = request.ReadAccess,
                    WriteAccess     = request.WriteAccess,
                    AllowParentless = parentId == null,
                    Parent          = parentFolder,
                    OwnerId         = user.Id,
                };

                await database.StorageItems.AddAsync(existingItem);
            }

            var version = await existingItem.CreateNextVersion(database);

            var file = await version.CreateStorageFile(database,
                                                       DateTime.UtcNow + AppInfo.RemoteStorageUploadExpireTime, request.Size);

            string?uploadUrl = null;
            MultipartFileUpload?multipart = null;
            long?  multipartId            = null;
            string?uploadId = null;

            if (request.Size >= AppInfo.FileSizeBeforeMultipartUpload)
            {
                // Multipart upload is recommended for large files, as large files are hard to make go through
                // in a reasonable time with a single PUT request
                try
                {
                    uploadId = await remoteStorage.CreateMultipartUpload(file.UploadPath, request.MimeType);

                    if (uploadId == null)
                    {
                        throw new Exception("returned uploadId is null");
                    }
                }
                catch (Exception e)
                {
                    logger.LogError("Failed to create multipart upload: {@E}", e);
                    return(Problem("Failed to create a new multipart upload"));
                }

                var chunks = ComputeChunksForFile(request.Size).ToList();
                var initialChunksToUpload = AddUploadUrlsToChunks(chunks.Take(AppInfo.MultipartSimultaneousUploads *
                                                                              AppInfo.MultipartUploadPartsToReturnInSingleCall), file.UploadPath, uploadId,
                                                                  AppInfo.RemoteStorageUploadExpireTime).ToList();

                var multipartModel = new InProgressMultipartUpload()
                {
                    UploadId       = uploadId,
                    Path           = file.UploadPath,
                    NextChunkIndex = initialChunksToUpload.Count,
                };

                await database.InProgressMultipartUploads.AddAsync(multipartModel);

                await database.SaveChangesAsync();

                multipartId = multipartModel.Id;

                var chunkToken = new ChunkRetrieveToken(multipartModel.Id, file.Id, uploadId);

                var chunkTokenStr = JsonSerializer.Serialize(chunkToken);

                multipart = new MultipartFileUpload()
                {
                    ChunkRetrieveToken =
                        chunkDataProtector.Protect(chunkTokenStr, AppInfo.MultipartUploadTotalAllowedTime),
                    TotalChunks = chunks.Count,
                    NextChunks  = initialChunksToUpload,
                };
            }
            else
            {
                // Normal upload (in a single PUT request)
                await database.SaveChangesAsync();

                uploadUrl = remoteStorage.CreatePresignedUploadURL(file.UploadPath,
                                                                   AppInfo.RemoteStorageUploadExpireTime);
            }

            // Need to queue a job to calculate the parent folder size
            if (parentId != null)
            {
                jobClient.Enqueue <CountFolderItemsJob>((x) => x.Execute(parentId.Value,
                                                                         CancellationToken.None));
            }

            if (uploadId != null)
            {
                jobClient.Schedule <DeleteNonFinishedMultipartUploadJob>((x) => x.Execute(uploadId,
                                                                                          CancellationToken.None), AppInfo.MultipartUploadTotalAllowedTime * 2);
            }

            // TODO: queue a job to delete the version / UploadPath after a few hours if the upload fails

            var token = new UploadVerifyToken()
            {
                TargetStorageItem        = existingItem.Id,
                TargetStorageItemVersion = version.Id,
                MultipartId = multipartId,
            };

            var tokenStr = JsonSerializer.Serialize(token);

            return(new UploadFileResponse()
            {
                UploadURL = uploadUrl,
                Multipart = multipart,
                TargetStorageItem = existingItem.Id,
                TargetStorageItemVersion = version.Id,
                UploadVerifyToken = dataProtector.Protect(tokenStr,
                                                          multipart == null ?
                                                          AppInfo.RemoteStorageUploadExpireTime :
                                                          AppInfo.MultipartUploadTotalAllowedTime),
            });
        }