Skip to content

Instantly share code, notes, and snippets.

@michaelchart
Last active January 13, 2017 10:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save michaelchart/e7785bfecc629f02fa19caf8f1736d25 to your computer and use it in GitHub Desktop.
Save michaelchart/e7785bfecc629f02fa19caf8f1736d25 to your computer and use it in GitHub Desktop.
Migrating your Umbraco 4 media library to Umbraco 7
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using Newtonsoft.Json;
using umbraco;
using Umbraco.Web.BaseRest;
using umbraco.cms.businesslogic.media;
namespace Umbraco4Site.Web.Services
{
/// Umbraco 4 Base class to provide endpoints for working with media
[RestExtension("Media")]
public class BaseMedia
{
/// <summary>
/// Method to generate a JSON export of the media within a certain folder.
/// Uses Member authentication with the "MediaExporter" member group
/// Authentication can be turned off by removing the AllowAll = false, AllowGroup = "MediaExporter" attributes below
/// </summary>
[RestExtensionMethod(ReturnXml = false, AllowAll = false, AllowGroup = "MediaExporter")]
public static string Export()
{
var context = HttpContext.Current;
// must specify a folderId in the querystring, e.g. ?folderId=1234
var folderId = context.Request.QueryString["folderId"];
if (string.IsNullOrWhiteSpace(folderId))
{
context.Response.StatusCode = 400;
return "Must specify folder ID";
}
// load the root media folder
var root = uQuery.GetMedia(folderId);
if (root == null)
{
context.Response.StatusCode = 404;
return "Folder not found";
}
// the base url to use for making an absolute URL from each media item's url path
var baseMediaUrl = context.Request.Url.GetLeftPart(UriPartial.Authority);
// get all media items within the given folder, convert to a simple DTO and sort by level so that folders can be created before their child items
var dto = root.GetDescendantOrSelfMedia().Select(m => ToDTO(m, baseMediaUrl)).OrderBy(m => m.Level);
context.Response.ContentType = "application/json";
return JsonConvert.SerializeObject(dto);
}
/// <summary>
/// Method to generate convert a media item to a simple Data Transfer Object
/// </summary>
private static MediaDTO ToDTO(Media media, string baseMediaUrl)
{
if (media == null) return null;
var filePath = media.GetProperty<string>("umbracoFile");
return new MediaDTO
{
Id = media.Id,
Name = media.Text,
Type = media.ContentType == null ? null : media.ContentType.Alias,
Level = media.Level,
Url = string.IsNullOrWhiteSpace(filePath) ? string.Empty : baseMediaUrl + filePath,
ParentId = media.ParentId,
Properties = media.GenericProperties.ToDictionary(p => p.PropertyType.Alias, p => p.Value.ToString())
};
}
}
public class MediaDTO
{
public int Id { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public string Url { get; set; }
public int ParentId { get; set; }
public int Level { get; set; }
public Dictionary<string, string> Properties { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web;
using System.Web.Http;
using Newtonsoft.Json;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Forms.Core;
using Umbraco.Web.WebApi;
using File = System.IO.File;
namespace Umbraco7Site.Web.Controllers.API
{
// Umbraco 7 API controller providing some endpoints to work with media
// Uses Umbraco backoffice authentication which can be disabled by changing this class to inherit from UmbracoApiController instead of UmbracoAuthorizedApiController
public class MediaToolsController : UmbracoAuthorizedApiController
{
private const string ImportListFile = "~/App_Data/MediaImport/imported.json";
/// <summary>
/// Takes media items in a JSON structure exported from an Umbraco 4 site, and downloads and imports all the items into this Umbraco site:
/// [{"Id":-1,"Level":0,"Name":"SYSTEM DATA: umbraco master root","ParentId":-1,"Properties":{},"Type":null,"Url":""},{"Id":3729,"Level":1,"Name":"Homepage images","ParentId":-1,"Properties":{},"Type":"Folder","Url":""},{"Id":19268,"Level":6,"Name":"Homepage Banner","ParentId":3729,"Properties":{"altText":"","cropper":"","umbracoBytes":"375969","umbracoExtension":"png","umbracoFile":"/media/928758/homepage-banner.png","umbracoHeight":"360","umbracoWidth":"955"},"Type":"Image","Url":"http://www.example.com/media/928758/homepage-banner.png"}]
/// </summary>
/// <param name="media"></param>
/// <returns></returns>
public string Import([FromBody] IEnumerable<MediaDTO> media)
{
Logger.Info(typeof(MediaToolsController), "Starting media import");
// use the Umbraco MediaService API
var mediaService = Services.MediaService;
// store a list of already imported media items in a file, so that if the importer fails, we can continue where we left off
var importFilePath = HttpContext.Current.Server.MapPath(ImportListFile);
var imported = LoadImportList(importFilePath);
// order by level to ensure parent folders are created before their children are imported
media = media.OrderBy(m => m.Level);
var count = 0;
foreach (var item in media)
{
Logger.Debug(typeof(MediaToolsController), $"Processing item {item.Id}");
if (imported.ContainsKey(item.Id))
{
Logger.Debug(typeof(MediaToolsController), "Skipping item as it is already imported");
continue;
}
IMedia mediaItem;
try
{
using (var client = new WebClient())
{
// if we've already imported the parent item, find the ID of the newly created media item, otherwise use the root media folder (-1) as the parentId
var parentId = imported.ContainsKey(item.ParentId) ? imported[item.ParentId] : -1;
// create the new media item
mediaItem = mediaService.CreateMedia(item.Name, parentId, item.Type);
// if there's a file associated with this media item
if (!string.IsNullOrWhiteSpace(item.Url))
{
var fileName = Path.GetFileName(new Uri(item.Url).LocalPath);
// todo: could do this async?
var fileData = client.DownloadData(item.Url);
Logger.Debug(typeof(MediaToolsController), $"Downloaded file data ({item.Url})");
var uploadFile = new MemoryStream(fileData);
// put the file contents to the media item
mediaItem.SetValue(Constants.Conventions.Media.File, fileName, uploadFile);
}
mediaService.Save(mediaItem);
Logger.Debug(typeof(MediaToolsController), $"Saved media item (new media ID {mediaItem.Id})");
}
}
catch (Exception ex)
{
Logger.Warn(typeof(MediaToolsController), $"Failed to download and import item {item.Id} ({ex.Message})");
continue;
}
// record the ID of the new media item against the old media item ID
imported[item.Id] = mediaItem.Id;
// save our import list to a file in case we need to rerun the import from where we left off
SaveImportList(importFilePath, imported);
count++;
}
Logger.Info(typeof(MediaToolsController), $"Imported {count} media items");
return $"Imported {count} media items";
}
// load the list of already imported media items with their old->new ID mapping
private Dictionary<int, int> LoadImportList(string importFilePath)
{
if (!File.Exists(importFilePath))
{
Logger.Info(typeof(MediaToolsController), "No previous import log");
return new Dictionary<int, int>();
}
else
{
Logger.Info(typeof(MediaToolsController), "Loading previous import log");
var json = File.ReadAllText(importFilePath);
Logger.Debug(typeof(MediaToolsController), $"Previous import log: {json}");
return JsonConvert.DeserializeObject<Dictionary<int, int>>(json);
}
}
// save our import list to a file in case we need to rerun the import from where we left off
private void SaveImportList(string importFilePath, Dictionary<int, int> importList)
{
var json = JsonConvert.SerializeObject(importList);
var directory = Path.GetDirectoryName(importFilePath);
if (directory != null) Directory.CreateDirectory(directory);
File.WriteAllText(importFilePath, json);
}
}
public class MediaDTO
{
public int Id { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public string Url { get; set; }
public int ParentId { get; set; }
public int Level { get; set; }
public Dictionary<string, string> Properties { get; set; }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment