Story of File Uploading in ASP.NET Core (Part III - Streaming Files)

We must consider saving files in byte[] formats unless it is absolute necessary, because over the time it can affect the performance of any data storage system. With that being said, streaming can be a good idea in this scenario.

We can configure a static files folder for storing the uploaded files. However, this part is optional. You can simply use the web root folder wwwroot instead. But let's just see how to create a separate static files folder on the content root and use it to serve the uploaded files.

The following code configuration code is placed in the Configure method of the Startup.cs file,

app.UseStaticFiles(); 

app.UseStaticFiles(new StaticFileOptions {
    FileProvider = new PhysicalFileProvider(Path.Combine(env.ContentRootPath, "Uploads")),
    RequestPath = new PathString("/Uploads")
});

Notice, along with the default app.UseStaticFiles(); we have an additional app.UseStaticFiles(); which configures a folder named Uploads to be a custom static files folder. The Uploads folder is available in the content root directory of the application,

Files under the Uploads folder can be accessed at will on a url like www.<app-url>.com/Uploads/<filename> (example: localhost:5000/Uploads/Avatar.png), hence the RequestPath configuration.

Next, we have to modify the Create action of UserMvcController and PostUser action of UserController. But first change the type of Avatar property from byte[] to string of the User entity class. From now on we will use the property to store the file name of the uploaded file.

User.cs
public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Avatar { get; set; }
}

The following code will stream the uploaded file in the Uploads directory and save the FileName (Available in IFormFile) in the Avatar property,

UserMvcController.cs
public async Task<IActionResult> Create([FromForm] UserVM vm)
{
    if (ModelState.IsValid)
    {
        var filePath = Path.Combine(_environment.ContentRootPath, "Uploads", vm.Avatar.FileName);

        using (var stream = new FileStream(filePath, FileMode.Create))
        {
            await vm.Avatar.CopyToAsync(stream);
        }

        User user = new User
        {
            Name = vm.Name,
            Avatar = vm.Avatar.FileName
        };

        _context.Add(user);
        await _context.SaveChangesAsync();
        return RedirectToAction(nameof(Index));
    }
    return View(vm);
}
UserController.cs
public async Task<IActionResult> PostUser([FromForm]UserVM vm)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }

    var filePath = Path.Combine(_environment.ContentRootPath, "Uploads", vm.Avatar.FileName);

    using (var stream = new FileStream(filePath, FileMode.Create))
    {
        await vm.Avatar.CopyToAsync(stream);
    }

    User user = new User
    {
        Name = vm.Name,
        Avatar = vm.Avatar.FileName
    };

    _context.Users.Add(user);
    await _context.SaveChangesAsync();

    return CreatedAtAction("GetUser", new { id = user.Id }, user);
}

Notice, we have constructed a file path for the newly uploaded file name. Later using a FileStream we are creating the file i.e. File.Create. Last of all we are copying the content of the uploaded file to the opened file stream.

And that's it. The project repository contains all the code shown above. If you clone and run the project, you can upload files using one of the controller's create user action. Following shows the Angular way of uploading files,

Note: To keep things simple, I didn't talk about file validations and it's a topic for the next post.

https://github.com/fiyazbinhasan/AspNetCore-AngularSpa-Playground