(C# ASP.NET Core) Uploading file(s) and Storing in a Database

This is a quick and clean scheme for uploading files to a server. In the first part we demonstrate how to store them into a database table. And, in the second we have a word of caution about putting them into a database. A better strategy would, instead, be to store them on a disk, possibly on a separate partition, and a method for doing this is also explained in the end.

Categories | About |     |  

Parveen,

How does it work?

A form is created with an input of type="file", and say, a name="mylist"


// asp tag helpers are not used 
// so name attribute must be explicitly set 

<input type="file"
         name="mylist"
         required
         multiple
         accept="application/pdf, image/png" />

When a user selects the files and submits them, then the uploaded files are received as an IList of IFormFile objects as shown below:

// IMPORTANT: the parameter "mylist" should be 
// of the same exact spellings as 
// the "name" attribute on the input element 
public async Task<IActionResult> OnPostAsync(List<IFormFile> mylist)
{

  // run a foreach and 
  // save the bytes to database or to hard-disk 
}

Video Explanation

Please watch the following youtube video:

Step 1 of 3: Create a Form

Write a form that contains an input of type="file" and specifically set its name attribute. We have to set the "name" parameter because tag helpers are not used.

IMPORTANT: Take these precautions:

  1. form must have its enctype set as enctype="multipart/form-data"
  2. input element must have multiple attribute to allow upload of multiple files.
  3. input element can have accept attribute to restrict the uploads to specific mime types.

@page "{handler?}"

@model Modular.Pages.IndexModel

<h1>Upload a PDF file < 1 MB</h1>

@if (TempData.ContainsKey("Message"))
{
  <small style="color:red">

    @TempData["Message"]

  </small>
}

<form method="post" enctype="multipart/form-data">

  <input type="file"
         name="files"
         required
         multiple
         accept="application/pdf, image/png" />

  <p />

  <input type="submit" value="Upload!" />

</form>

Step 2 of 3: Setup a Model

NOTE: it is assumed that database set up is already done

Write a model class to hold the bytes of the uploaded file.

using Microsoft.EntityFrameworkCore;

using System;

using System.ComponentModel.DataAnnotations;

using System.ComponentModel.DataAnnotations.Schema;

using System.Diagnostics.CodeAnalysis;

namespace DBaseCon
{

  public class UploadedFile
  {

    // auto-increment Primary ID key 
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int ID { get; set; }

    // this column is for a file BLOB 
    public byte[] FileBytes { get; set; }
  }


  // -------- DbContext ---------- // 

  // dbcontext DAL - THERE IS ONE DbContext per database 
  public class ProjectContext : DbContext
  {

    public ProjectContext(DbContextOptions options) : base(options)
    {

      // EnsureCreated is meant for testing or creating 
      // a blank database. this code should be avoided 
      // at this place. use some alternate program to 
      // create/migrate your database. this code has been 
      // used here only for tutorial purposes 
      Database.EnsureCreated();

    }

    // this function is used to specify FK relations, Indexes, 
    // and [optionally] the name of your database tables 
    // corresponding to each model 
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {

      base.OnModelCreating(modelBuilder);

      // name of the table in your database 
      modelBuilder.Entity<UploadedFile>().ToTable("product");

    }

    // MUST be PLURAL 
    public DbSet<UploadedFile> Files { get; set; }
  }

}

Step 3 of 3: Complete the backing class

We have used TempData for passing validation errors.

using DBaseCon;

using Microsoft.AspNetCore.Http;

using Microsoft.AspNetCore.Mvc;

using Microsoft.AspNetCore.Mvc.RazorPages;

using Microsoft.EntityFrameworkCore;

using System.Collections.Generic;

using System.IO;

using System.Threading.Tasks;

namespace Modular.Pages
{

  public class IndexModel : PageModel
  {

    private readonly ProjectContext _ctx;

    // dependency injection of the ProductContext 
    public IndexModel(ProjectContext ctx)
    {

      _ctx = ctx;

    }

    // handy tool for validation error 
    [TempData]
    public string Message { get; set; }

    // handles the form post 
    public async Task<IActionResult> OnPostAsync(List<IFormFile> mylist)
    {

      if(null != mylist)
      {

        foreach (IFormFile myfile in mylist)
        {

          if (
            (myfile.Length > 1024 * 1000)
            ||
            (!"application/pdf".Equals(myfile.ContentType))
          )
          {

            Message = "File must be pdf type max 1 MB. Ignored.";

            continue;

          }

          else 
          {

            var uploadedFile = new UploadedFile();

            using (MemoryStream ms = new MemoryStream())

            {

              // copy the file to memory stream 
              await myfile.CopyToAsync(ms);

              // set the byte array 
              uploadedFile.FileBytes = ms.ToArray();

            }

            _ctx.Files.Add(uploadedFile);

            await _ctx.SaveChangesAsync();

          } //~else ends

        }//~foreach ends

      }

      return RedirectToPage();

    }

  }

}

A word of caution

The above code explains how to save a file into a database.

I would personally never do that unless the files are small, of a few bytes and not very important. Stuffing files into a single database - and that too - if the same database holds other critical data - does hold a risk of corruption, something capable of giving sleepless nights.

A better approach would be to save the files to folders on your server. They should, however, be renamed, say to some unique ID, and make sure that a virus scan sanitizes each of them. Such a scheme is safer. Moreover, a server side application can be scheduled to take a second backup.

How to save to a disk?

Modify the code like so

public async Task<IActionResult> OnPostAsync(List<IFormFile> mylist)
{

  if(null != mylist)
  {

    foreach (IFormFile myfile in mylist)
    {

      if (
        (myfile.Length > 1024 * 1000)
        ||
        (!"application/pdf".Equals(myfile.ContentType))
      )
      {

        Message = "File must be pdf type max 1 MB. Ignored.";

        continue;

      }

      else 
      {

        // generate your own name, say from an ID 
        // or use Path.GetRandomFileName() 
        // and store that in a database 
        // it will keep your database small 
        var filePath = Path.Combine("Folder", "some-name");

        using (var stream = System.IO.File.Create(filePath))

        {

          await myfile.CopyToAsync(stream);

        }

      }

    }

  }

  return RedirectToPage();

}


This Blog Post/Article "(C# ASP.NET Core) Uploading file(s) and Storing in a Database" by Parveen is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.

Comments and Discussion