I have this code that will call an this API endpoint but this still doesn't worked. I'm not sure if this is the right path so any guidance is appreciated.
[HttpGet("get")]
[ProducesResponseType(typeof(FileResult), 200)]
[ProducesResponseType(typeof(object), 400)]
[ProducesResponseType(typeof(object), 404)]
[ProducesResponseType(typeof(object), 500)]
public IActionResult GetFile([FromQuery] string filename)
{
try
{
// Security: Validate input
if (string.IsNullOrWhiteSpace(filename))
{
_eventLog.LogWarning("FormFileApi", "INVALID-INPUT", "Empty filename provided");
return BadRequest(new { error = "Filename is required" });
}
// URL decode the filename (spaces, special chars may be encoded)
var decodedFilename = Uri.UnescapeDataString(filename);
_eventLog.LogInformation("FormFileApi", "FILENAME-RECEIVED", $"Original: {filename}, Decoded: {decodedFilename}");
// Security: Sanitize and validate filename
var sanitizedFilename = SanitizeFilename(decodedFilename);
_eventLog.LogInformation("FormFileApi", "FILENAME-SANITIZED", $"Sanitized: {sanitizedFilename}");
if (string.IsNullOrEmpty(sanitizedFilename))
{
_eventLog.LogWarning("FormFileApi", "INVALID-FILENAME", $"Invalid filename format: {filename}");
return BadRequest(new { error = "Invalid filename format" });
}
// Security: Check filename length
if (sanitizedFilename.Length > MaxFilenameLength)
{
_eventLog.LogWarning("FormFileApi", "FILENAME-TOO-LONG", $"Filename too long: {sanitizedFilename.Length} characters");
return BadRequest(new { error = "Filename is too long" });
}
// Security: Validate file extension
var extension = System.IO.Path.GetExtension(sanitizedFilename);
if (string.IsNullOrEmpty(extension) || !AllowedExtensions.Contains(extension))
{
_eventLog.LogWarning("FormFileApi", "INVALID-EXTENSION", $"Invalid file extension: {extension}");
return BadRequest(new { error = "File type not allowed" });
}
// Search for the file in form submissions
var fileResult = FindFileInSubmissions(sanitizedFilename);
if (fileResult == null)
{
_eventLog.LogInformation("FormFileApi", "FILE-NOT-FOUND", $"File not found: {sanitizedFilename}");
return NotFound(new { error = "File not found" });
}
// Security: Log successful access
_eventLog.LogInformation("FormFileApi", "FILE-ACCESSED",
$"File accessed: {sanitizedFilename}, SubmissionID: {fileResult.SubmissionItemID}, FormID: {fileResult.FormID}");
// Get virtual file path for Azure Storage using SystemFileName (GUID)
// Files are stored as ~/assets/BizFormFiles/{SystemFileName}
// SystemFileName is the GUID filename used in Azure Storage
if (string.IsNullOrWhiteSpace(fileResult.SystemFileName))
{
_eventLog.LogWarning("FormFileApi", "MISSING-SYSTEM-FILENAME",
$"SystemFileName is missing for file: {sanitizedFilename}");
return NotFound(new { error = "File not found" });
}
// Construct virtual path - SystemFileName is the GUID filename
// Note: ~/assets/ is mapped to Azure Storage in StorageInitializationModule
// Try multiple path variations as files might be stored differently
var systemFileName = fileResult.SystemFileName;
// Try different path variations
var pathVariations = new List<string>
{
$"/assets/BizFormFiles/{systemFileName}", // Standard path with extension
$"/assets/bizformfiles/{systemFileName}", // Lowercase folder name
$"/assets/BizFormFiles/{systemFileName.ToLowerInvariant()}", // Lowercase filename
};
// If SystemFileName has an extension, also try without it
if (systemFileName.Contains('.'))
{
var guidWithoutExt = systemFileName.Substring(0, systemFileName.LastIndexOf('.'));
pathVariations.Add($"/assets/BizFormFiles/{guidWithoutExt}");
pathVariations.Add($"/assets/bizformfiles/{guidWithoutExt}");
}
_eventLog.LogInformation("FormFileApi", "ATTEMPTING-FILE-ACCESS",
$"Attempting to access file: OriginalFileName={fileResult.OriginalFileName}, SystemFileName={systemFileName}, Trying {pathVariations.Count} path variations");
CMS.IO.FileInfo? fileInfo = null;
string? actualFilePath = null;
// Try each path variation until we find the file
foreach (var virtualFilePath in pathVariations)
{
// Security: Verify the file is within the BizFormFiles directory (prevent path traversal)
if (!virtualFilePath.StartsWith("/assets/", StringComparison.OrdinalIgnoreCase) ||
(!virtualFilePath.Contains("BizFormFiles", StringComparison.OrdinalIgnoreCase) &&
!virtualFilePath.Contains("bizformfiles", StringComparison.OrdinalIgnoreCase)))
{
continue; // Skip invalid paths
}
try
{
fileInfo = CMS.IO.FileInfo.New(virtualFilePath);
if (fileInfo != null && fileInfo.Exists)
{
actualFilePath = virtualFilePath;
_eventLog.LogInformation("FormFileApi", "FILE-FOUND-VARIATION",
$"File found using path variation: {virtualFilePath}");
break;
}
}
catch (Exception ex)
{
_eventLog.LogException("FormFileApi", "FILE-INFO-CREATION-ERROR", ex,
additionalMessage: $"VirtualPath: {virtualFilePath}");
}
}
if (fileInfo == null || !fileInfo.Exists || string.IsNullOrEmpty(actualFilePath))
{
// Try to list files in the BizFormFiles directory to help debug
try
{
// Extract GUID from SystemFileName (remove extension if present)
var guidToFind = systemFileName;
if (systemFileName.Contains('.'))
{
guidToFind = systemFileName.Substring(0, systemFileName.LastIndexOf('.'));
}
var directoriesToCheck = new[] { "/assets/BizFormFiles", "/assets/bizformfiles" };
foreach (var dirPath in directoriesToCheck)
{
var dirInfo = CMS.IO.DirectoryInfo.New(dirPath);
if (dirInfo.Exists)
{
// Get all files
var allFiles = dirInfo.GetFiles();
var fileNames = allFiles.Select(f => f.Name).Take(20).ToList();
// Search for files containing the GUID
var matchingFiles = allFiles.Where(f =>
f.Name.Contains(guidToFind, StringComparison.OrdinalIgnoreCase)).ToList();
_eventLog.LogInformation("FormFileApi", "DIRECTORY-LISTING",
$"Directory {dirPath} exists. Total files: {allFiles.Length}. Files matching GUID '{guidToFind}': {matchingFiles.Count}. " +
$"Matching files: {string.Join(", ", matchingFiles.Select(f => f.Name))}. " +
$"First 20 files: {string.Join(", ", fileNames)}");
// If we found matching files, try using the actual filename from the directory
if (matchingFiles.Any())
{
foreach (var matchingFile in matchingFiles)
{
var actualPath = $"{dirPath}/{matchingFile.Name}";
try
{
var testFileInfo = CMS.IO.FileInfo.New(actualPath);
if (testFileInfo != null && testFileInfo.Exists)
{
_eventLog.LogInformation("FormFileApi", "FOUND-MATCHING-FILE",
$"Found matching file: {actualPath}, Size: {testFileInfo.Length} bytes");
// Use this file
fileInfo = testFileInfo;
actualFilePath = actualPath;
break;
}
}
catch (Exception ex)
{
_eventLog.LogException("FormFileApi", "TEST-FILE-ACCESS-ERROR", ex,
additionalMessage: $"Path: {actualPath}");
}
}
if (fileInfo != null && fileInfo.Exists && !string.IsNullOrEmpty(actualFilePath))
{
break; // Found the file, exit the directory loop
}
}
}
}
if (fileInfo == null || !fileInfo.Exists || string.IsNullOrEmpty(actualFilePath))
{
_eventLog.LogWarning("FormFileApi", "DIRECTORY-NOT-FOUND-OR-EMPTY",
"BizFormFiles directory does not exist or is empty in both cases");
}
}
catch (Exception ex)
{
_eventLog.LogException("FormFileApi", "DIRECTORY-LISTING-ERROR", ex);
}
_eventLog.LogWarning("FormFileApi", "FILE-NOT-FOUND-STORAGE",
$"File not found in storage after trying {pathVariations.Count} variations. OriginalFileName={fileResult.OriginalFileName}, SystemFileName={systemFileName}, Tried paths: {string.Join(", ", pathVariations)}");
return NotFound(new { error = "File not found on server" });
}
_eventLog.LogInformation("FormFileApi", "FILE-FOUND",
$"File found in storage: VirtualPath={actualFilePath}, Size={fileInfo.Length} bytes");
// Get file content type from original filename
var contentType = GetContentType(fileResult.OriginalFileName);
// Read file from Azure Storage using CMS.IO (works with mapped storage providers)
byte[] fileBytes;
using (var fileStream = IOFile.OpenRead(actualFilePath))
{
fileBytes = new byte[fileStream.Length];
fileStream.Read(fileBytes, 0, (int)fileStream.Length);
}
// Use original filename for download
return File(fileBytes, contentType, fileResult.OriginalFileName);
}
catch (Exception ex)
{
_eventLog.LogException("FormFileApi", "GET-FILE-ERROR", ex,
additionalMessage: $"Filename: {filename}");
return StatusCode(500, new { error = "An error occurred while retrieving the file", details = ex.Message });
}
}