I want to create an attribute which will perform following tasks.
- Validate the file type as per the file extension.
- Validate the file type as per magic number/signature of file.
- Validate the file length/size.
If file is validated then I want to store the file into database as as Base 64 string.
I tried it, but in my solution I have to read file twice first in attribute to check extension, magic number and size and secondly to convert the file stream into base 64 string. But Request.Content is forward only reader so it is throwing error when I tried to read the file again.
Please see the code below
File Validator Filter
public class ValidateFileAttribute : ActionFilterAttribute
{
public override Task OnActionExecutingAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
{
return Task.Factory.StartNew(async () => {
if (Request.Content.IsMimeMultipartContent())
{
var provider = actionContext.Request.Content.ReadAsMultipartAsync(cancellationToken).Result;
foreach (var content in provider.Contents)
{
//Here logic to check extension, magic number and length.
//If any error occurred then throw exception with HttpStatusCode
var fileName = content.Headers.ContentDisposition == null ? string.Empty : content.Headers.ContentDisposition.FileName;
var fileInBytes = content.ReadAsByteArrayAsync().Result;
var extention = fileName.Substring(fileName.LastIndexOf('.') + 1);
var validExtensions = new List<string>() { "pdf", "doc", "docx" };
if (!validExtensions.Contains(extention, StringComparer.OrdinalIgnoreCase))
{
//Return Exception
}
if (fileInBytes != null && fileInBytes.Any())
{
var magicNumber = BitConverter.ToString(fileInBytes).Substring(0, 11);
var validMagicNumbers = new List<string>() { "25-50-44-46", "D0-CF-11-E0", "50-4B-03-04" };
if (!validMagicNumbers.Contains(magicNumber, StringComparer.OrdinalIgnoreCase))
{
// Return Exception
}
}
if(fileInBytes != null && fileInBytes.Any() && fileInBytes.Length >= 3000000)
{
// Return Exception
}
}
}
}, cancellationToken);
}
}
Upload Action Method
[ValidateFile]
[Route("upload")]
[HttpPost]
public DocumentUploadResponse Upload()
{
if (Request.Content.IsMimeMultipartContent())
{
var provider = Request.Content.ReadAsMultipartAsync().Result;
// Getting error here..
foreach (var content in provider.Contents)
{
//Here logic to convert file stream into base 64 string.
//And store that string into Database.
var fileInBytes = content.ReadAsByteArrayAsync().Result;
var fileToStore = Convert.ToBase64String(fileInBytes);
/// Here goes Database code.....
}
}
}
Your help will be appreciated.