0

I'm trying to validate that only .jpg file types can be uploaded into my local file storage and then include the URL of that location in the database.

I would like to check for the validation on each of the files that I'm trying to upload and if either file does NOT include .jpg then I would like to halt the application from moving forward with a message stating "Invalid file format".

I have the following code below. At this time I'm not getting any error messages, but rather the page will just refresh without displaying an error or message and it will also not proceed with the correct file format.

Controller

[HttpPost]
    [ValidateAntiForgeryToken]
    public IActionResult Upsert(DepartmentPageCreateVM departmentPageVM, List<IFormFile> files)
    {
        if (ModelState.IsValid)
        {
            if (departmentPageVM.DepartmentPage.DepartmentPageId == 0)
            {
                _context.DepartmentPages.Add(departmentPageVM.DepartmentPage);
            }
            else
            {
                _context.DepartmentPages.Update(departmentPageVM.DepartmentPage);
            }
            _context.SaveChanges();

            string wwwRootPath = _webHostEnvironment.WebRootPath;
            if (files != null)
            {
                foreach (IFormFile file in files)
                {
                    //string productPath = @"images\departments\department-" + departmentPageVM.DepartmentPage.DepartmentPageId;
                    string fileName = Guid.NewGuid().ToString() + Path.GetExtension(file.FileName);
                    string productPath = @"images/departments/department-" + departmentPageVM.DepartmentPage.DepartmentPageId;
                    string finalPath = Path.Combine(wwwRootPath, productPath);

                    if (!Directory.Exists(finalPath))
                        Directory.CreateDirectory(finalPath);

                    // check file extension
                    string extFile = Path.GetExtension(fileName);
                    if (extFile != ".jpg")
                    {
                        ModelState.AddModelError("", "Invalid file format");
                    }

                    using (var fileStream = new FileStream(Path.Combine(finalPath, fileName), FileMode.Create))
                    {
                        file.CopyTo(fileStream);
                    }

                    DepartmentPhoto departmentPhoto = new()
                    {
                        //DepartmentPhotoImageURL = @"\" + productPath + @"\" + fileName,
                        DepartmentPhotoImageURL = @"/" + productPath + @"/" + fileName,
                        DepartmentPageId = departmentPageVM.DepartmentPage.DepartmentPageId,
                    };

                    if (departmentPageVM.DepartmentPage.DepartmentPhoto == null)
                        departmentPageVM.DepartmentPage.DepartmentPhoto = new List<DepartmentPhoto>();

                    departmentPageVM.DepartmentPage.DepartmentPhoto.Add(departmentPhoto);
                }
                _context.DepartmentPages.Update(departmentPageVM.DepartmentPage);
                _context.SaveChanges();
            }
            return RedirectToAction("Index");
        }
        else
        {
            departmentPageVM.DepartmentList = _context.Departments.ToList().Select(u => new SelectListItem
            {
                Text = u.DepartmentName,
                Value = u.DepartmentId.ToString()
            });
            return View(departmentPageVM);
        }
    }

File Input in View

<div class="form-group mt-4">
            <label asp-for="DepartmentPage.DepartmentPhoto"></label>
            <input type="file" name="files" class="form-control" multiple />
        </div>

ViewModel

public class DepartmentPageCreateVM
{
    public DepartmentPage DepartmentPage { get; set; }

    [ValidateNever]
    public IEnumerable<SelectListItem> DepartmentList { get; set; }
}

DepartmentPhoto Model

public class DepartmentPhoto
{
    public int DepartmentPhotoId { get; set; }
    public string? DepartmentPhotoImageURL { get; set; }

    public int DepartmentPageId { get; set; }
    public DepartmentPage? DepartmentPage { get; set; }
}

DepartmentPage Model

public class DepartmentPage
{
    public int DepartmentPageId { get; set; }
    public string Name { get; set; }
    public string ContentBody { get; set; }

    public int DepartmentId { get; set; }

    [ValidateNever]
    public Department Department { get; set; }

    [ValidateNever]
    public List<DepartmentPhoto> DepartmentPhoto { get; set; }
}
  • Try : string filename = "abcdefghijk.jpg"; int index = filename.LastIndexOf("."); string ext = filename.Substring(index + 1); – jdweng May 26 '23 at 14:08
  • So, what is the problem? Identifying non-jpeg files or showing an error message? – Fildor May 26 '23 at 14:11
  • BTW, I can take "malicious.exe" and rename it to "cutebunny.jpg"... just saying. – Fildor May 26 '23 at 14:17
  • @Fildor, I need to prevent files formats except for .jpg file types. If file the file type is not .jpg then display error and prevent applications from adding image URL to DB and prevent file from getting added to file storage. – user18776105 May 26 '23 at 14:17
  • @Fildor, when files get uploaded they will have a unique filename. I just need to validate the file type then file size. But for now I just need help with file type. – user18776105 May 26 '23 at 14:19
  • Ok, so I take that as "both". Does just checking the extension actually work for you or do you need to be _sure_ it **is** a jpeg? – Fildor May 26 '23 at 14:19
  • @Fildor, neither is working for me at the moment. I'm also not getting any errors which has made it more difficult to troubleshoot. Also, user can add multiple files to upload, so I would need to verify that each file getting uploaded is ONLY a .jpg or .jpeg file type. – user18776105 May 26 '23 at 14:21
  • Ok, so checking the extension is kind of sane and easy. On top of that, you can check the magic header : https://en.wikipedia.org/wiki/List_of_file_signatures but you would have to have the actual content to do that, so that's maybe for an intermediate stage if you cannot do it on the clinet side. – Fildor May 26 '23 at 14:24
  • @jdweng, I tried to add that but no luck. Nothing happens. – user18776105 May 26 '23 at 14:27
  • @Fildor, I have to be missing something super easy with this. I just can't seem to figure out what and were to place to check. Been banging my head for the last few days. – user18776105 May 26 '23 at 14:28
  • Seems ok, where it is - at least for the easy extension check. I think you are just missing a `continue;` inside the if-block, so invalid files won't be handled like valid ones. – Fildor May 26 '23 at 14:35
  • Are you testing with/without period? – jdweng May 26 '23 at 14:46
  • @jdweng, I've tried my code and what you provided with/without a period and it didn't make any difference. – user18776105 May 26 '23 at 17:22
  • @Fildor, the original code I posted above, are you saying it looks good? If so, what would I need to add to the if statement and which if statement would I need to modify? – user18776105 May 26 '23 at 17:24
  • The code works. What ext are you getting? – jdweng May 26 '23 at 18:17
  • @jdweng, I apologize. I forgot to remove my code before adding your code. When I implement your code it adds the abcdefghijk.jpg file path to my database and adds a file to my local folder. – user18776105 May 26 '23 at 18:44
  • @jdweng, but no matter what I select to upload it keeps putting that file into the system, which I understand that may be to just test. But not sure how that will help me prevent other file types other than .jpg to be added to the file path and the URL to the database. – user18776105 May 26 '23 at 18:45
  • Try following : string[] fileTypes = { "jpg", "tif", "bmp" }; string filename = "abcdefghijk.jpg"; int index = filename.LastIndexOf("."); string ext = filename.Substring(index + 1); if(fileTypes.Contains(ext)) { Console.WriteLine("True"); } – jdweng May 26 '23 at 23:25

1 Answers1

0

From the code I think you need to return the view with model after adding the ModelState.AddModelError("", "Invalid file format"); in order to display the error.

You can also do saveChanges() after all images uploaded without any error as per best practice but you can ignore this advice if your requirement is different.

And better way to validate any file is :

  1. check content type of file.
  2. check the extension
  3. load the file in memory and check initial bits.

For image you can refer the code of this answer : Determine if uploaded file is image (any format) on MVC

Kunal
  • 75
  • 4
  • how would I be able to implement checking the content type and file extension with the code I have now. – user18776105 May 28 '23 at 19:19
  • You can check in for loop in which you are iterating files. Something like file.ContentType and for ext Path.GetExtension(file.FileName). Please check that given link in that user created a extention method on IFormFile, you may get idea around it. If you implement something like that then you need to call that extension method like file.IsImage(). – Kunal May 30 '23 at 06:40