0

I want to create an attribute which will perform following tasks.

  1. Validate the file type as per the file extension.
  2. Validate the file type as per magic number/signature of file.
  3. 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.

rajk
  • 221
  • 3
  • 19
  • Validation should be done using a `ValidationAttribute`, not an `ActionFilterAttribute` –  Jan 26 '18 at 23:45
  • @StephenMuecke - How do I validate Http Request using ValidationAttribute. I am not using any model here. – rajk Jan 26 '18 at 23:48
  • Why not use a model with a `HttpPostedFileBase` property? –  Jan 26 '18 at 23:50
  • Even if I use model how can check the file properties (name, length and magic number) ? I think to check these properties we have to read file first. Please correct me If I am wrong. – rajk Jan 26 '18 at 23:53
  • Refer [How to validate file type of HttpPostedFileBase attribute in Asp.Net MVC 4?](https://stackoverflow.com/questions/40199870/how-to-validate-file-type-of-httppostedfilebase-attribute-in-asp-net-mvc-4/40200034#40200034) for an example of a `[FileType]` attribute which also include a link to a `[FileSize]` attribute –  Jan 26 '18 at 23:56
  • Thanks @StephenMuecke , but if I change request of api then I need to change the implementation at client end as well. Because currently we are reading upload file from Request object itself. – rajk Jan 27 '18 at 00:04
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/163988/discussion-between-sandyk-and-stephen-muecke). – rajk Jan 27 '18 at 00:08

0 Answers0