How to upload a file to Umbraco from a front end form

Posted written by Paul Seal on July 03, 2020 Umbraco

In this post I show you how you can use a file upload input in a form and upload it to the media section of Umbraco.

As with my previous post, I learned how to do this when working on a member edit profile form for Tarik from Creativaholic Studio. Tarik has kindly agreed for me to be able to share my code with you to help you too.

The File Input

This is just a straight forward file input. You can add validation around the file extension and file size but for now we will trust that our user will upload small images.

<input type="file" name="Avatar" id="Avatar" />

The View Model

using System.ComponentModel.DataAnnotations;
using System.Web;

namespace CodeShare.Core.Models.ViewModels
{
    public class EditProfileViewModel
    {
        [Display(Name = "Avatar")]
        public HttpPostedFileBase Avatar { get; set; }
    }
}

I wanted to make the code reusable and available using Dependency Injection, so I created a service called MediaUploadService

IMediaUploadService Interface

using System;
using System.Web;
using Umbraco.Core.Models;

namespace CodeShare.Core.Services
{
    public interface IMediaUploadService
    {
        string CreateMediaItemFromFileUpload(HttpPostedFileBase file, int parentId, string mediaTypeAlias,
            int userId = -1);

        string CreateMediaItemFromFileUpload(HttpPostedFileBase file, Guid parentId, string mediaTypeAlias,
            int userId = -1);

        string CreateMediaItemFromFileUpload(HttpPostedFileBase file, IMedia parent, string mediaTypeAlias,
            int userId = -1);

        string SetMediaItemValueFromFileUpload(HttpPostedFileBase file, IMedia mediaItem);
    }
}

There are basically 2 methods on there, CreateMediaItemFromFileUpload and SetMediaItemValueFromFileUpload. The first one has 3 different signatures, just because of the 3 different ways to pass in the parent id or parent ojbject when creating a media item.

MediaUploadService Class

The idea here is that you create a media item first and then you use the uploaded file to set the value of the umbracoFile property on the media item. I've seen forum post answers about this sort of thing but they are usually vague and they assume you already know what to do. Hopefully this is clearer.

using System;
using System.Web;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Services;

namespace CodeShare.Core.Services
{
    public class MediaUploadService : IMediaUploadService
    {
        private readonly IMediaService _mediaService;
        private readonly IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider;

        public MediaUploadService(IMediaService mediaService,
            IContentTypeBaseServiceProvider contentTypeBaseServiceProvider)
        {
            _mediaService = mediaService;
            _contentTypeBaseServiceProvider = contentTypeBaseServiceProvider;
        }

        /// <summary>
        /// You pass in a file and it uploads it to the media section and returns the udi for the media item
        /// </summary>
        /// <param name="file">The file to upload</param>
        /// <param name="parentId">The id of the parent media item</param>
        /// <param name="mediaTypeAlias">The media type alias, e.g. Image, File, Folder</param>
        /// <returns>The udi for the new media item</returns>
        public string CreateMediaItemFromFileUpload(HttpPostedFileBase file, int parentId, string mediaTypeAlias, int userId = -1)
        {
            var mediaItem = _mediaService.CreateMedia(file.FileName, parentId, mediaTypeAlias);
            return SetMediaItemValueFromFileUpload(file, mediaItem);
        }

        /// <summary>
        /// You pass in a file and it uploads it to the media section and returns the udi for the media item
        /// </summary>
        /// <param name="file">The file to upload</param>
        /// <param name="parentId">The id of the parent media item to upload it to</param>
        /// <param name="mediaTypeAlias">The media type alias, e.g. Image, File, Folder</param>
        /// <returns>The udi for the new media item</returns>
        public string CreateMediaItemFromFileUpload(HttpPostedFileBase file, Guid parentId, string mediaTypeAlias, int userId = -1)
        {
            var mediaItem = _mediaService.CreateMedia(file.FileName, parentId, mediaTypeAlias);
            return SetMediaItemValueFromFileUpload(file, mediaItem);
        }

        /// <summary>
        /// You pass in a file and it uploads it to the media section and returns the udi for the media item
        /// </summary>
        /// <param name="file">The file to upload</param>
        /// <param name="parent">The parent media item</param>
        /// <param name="mediaTypeAlias">The media type alias, e.g. Image, File, Folder</param>
        /// <returns>The udi for the new media item</returns>
        public string CreateMediaItemFromFileUpload(HttpPostedFileBase file, IMedia parent, string mediaTypeAlias, int userId = -1)
        {
            var mediaItem = _mediaService.CreateMedia(file.FileName, parent, mediaTypeAlias);
            return SetMediaItemValueFromFileUpload(file, mediaItem);
        }

        /// <summary>
        /// You pass in a file and it uploads it to the media item
        /// </summary>
        /// <param name="file">The file to upload</param>
        /// <param name="mediaItem">The media item to upload the file to</param>
        /// <returns>The udi for the new media item</returns>
        public string SetMediaItemValueFromFileUpload(HttpPostedFileBase file, IMedia mediaItem)
        {
            mediaItem.SetValue(_contentTypeBaseServiceProvider, "umbracoFile", file.FileName, file.InputStream);

            _mediaService.Save(mediaItem);

            var udi = $"umb://media/{mediaItem.Key.ToString("N")}";
            return udi;
        }
    }
}

When you call the method to create the media item from the file upload, it returns the UDI for the media item. You can then set this as a property value for a media picker, or if you have multiple you can just comma separate them for multi content pickers.

Register the service for Dependency Injection usage

using CodeShare.Core.Services;
using CodeShare.Core.Services;
using Umbraco.Core;
using Umbraco.Core.Composing;

namespace CodeShare.Core.Composing
{
    [RuntimeLevel(MinLevel = RuntimeLevel.Run)]
    public class RegisterServicesComposer : IUserComposer
    {
        public void Compose(Composition composition)
        {
            composition.Register(typeof(IMediaUploadService),
                typeof(MediaUploadService), Lifetime.Request);
        }
    }
}

Here's how you would call the code from your surface controller, which is also set up to use the service, using Dependency Injection.

Surface Controller Action

using CodeShare.Core.Models.ViewModels;
using CodeShare.Core.Services;
using System.Web.Mvc;
using System.Web.Security;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
using Umbraco.Web.Mvc;

namespace CodeShare.Core.Controllers.Surface
{
    public class AccountSurfaceController : SurfaceController
    {
        private readonly IMediaUploadService _mediaUploadService;
        private readonly IMemberService _memberService;

        public AccountSurfaceController(IMediaUploadService mediaUploadService,
            IMemberService memberService)
        {
            _mediaUploadService = mediaUploadService;
            _memberService = memberService;
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult EditProfile(EditProfileViewModel model)
        {
            if (!ModelState.IsValid) return CurrentUmbracoPage();

            var member = GetMemberFromUser(Membership.GetUser());

            if (model.Avatar != null)
            {
                var avatarUdi = _mediaUploadService.CreateMediaItemFromFileUpload(model.Avatar, 1129, "Image");
                member.SetValue("avatar", avatarUdi);
            }

            return RedirectToCurrentUmbracoPage();
        }

        public IMember GetMemberFromUser(MembershipUser user)
        {
            return user != null ? _memberService.GetByUsername(user.UserName) : null;
        }
    }
}

Hopefully this has made it a lot easier for you when trying to upload files to the media section from the front end of a site, and it means you haven't had to go through the pain of learning how to do it.

If you need any help with implementing any of this, you can get in touch with me on LinkedIn.