Converting Sitecore file-based media items to database-based items with Sitecore Powershell Extensions

Sitecore provides two options for storing media items: store them in the Sitecore database, or in the file system. Sitecore recommends storing media items in the database. The advantage to storing in the file system is that it can improve performance especially when there are a ton of images on the site; however, the file-based items have given me problems on our CM/CD instances.

When a media item is uploaded as file-based, Sitecore will store the actual blob on disk and not in the database. It lives in the website’s App_Data folder, and shows the File Path in Sitecore:

File-based media item in Sitecore

The File Path shows the location on the CM server. However, when I publish this item, it publishes the Sitecore asset but does not move the file from the CM server to the CD server, so the image is missing on the CD instance unless I manually copy the file to the server.

You could write some custom code to automatically copy the file from one server to another on publish, but it’s much easier to just upload media items to the database instead of the file system.

However, suppose someone has already manually uploaded about 100 images into Sitecore as file-based items. Manually fixing all of these items is a tedious task, so I looked for a way to convert the items from file-based to database-based automatically. This can be accomplished with Sitecore Powershell Extensions.

I found this Stack Overflow post that almost gave me the correct answer, with one error in syntax. The function for getting all of the file-based items and the function for converting them both worked; the only issue was that the last line which called everything (Get-MediaItemFileBased | ConvertTo-MediaItemDatabaseBased) only executed the conversion on the last item. This is a syntax issue, explained by this article: “If you don’t specify Begin, Process and End blocks in your function, then the function code is automatically considered the End block.”

I fixed this by adding the For Each syntax, changing ConvertTo-MediaItemDatabaseBased to % { ConvertTo-MediaItemDatabaseBased $_ }

Below is the complete code, which will find and update all file-based media items in your media library. Note that if you want to restrict it to a specific folder, you can change the -Path on the second line to the folder of your choosing (-Path "master:\sitecore\media library")

function Get-MediaItemFileBased {
    Get-ChildItem -Path "master:\sitecore\media library" -Recurse | 
        Where-Object { $_.TemplateID -ne [Sitecore.TemplateIDs]::MediaFolder } |
        ForEach-Object {
            $mediaItem = New-Object Sitecore.Data.Items.MediaItem $PSItem
            if($mediaItem.FileBased) {
                $mediaItem
            }
        }
}

function ConvertTo-MediaItemDatabaseBased {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
        [ValidateNotNullOrEmpty()]
        [Sitecore.Data.Items.MediaItem]$MediaItem
    )

    if($MediaItem -eq $null) { continue }

    $filePath = [Sitecore.IO.FileUtil]::MapPath($MediaItem.FilePath)
    $fileInfo = Get-Item -Path $filePath
    if($fileInfo.Exists) {
        $mediaCreator = New-Object Sitecore.Resources.Media.MediaCreator
        $mediaItemFullPath = $MediaItem.InnerItem.Paths.Path
        $mediaCreatorOptions = New-Object Sitecore.Resources.Media.MediaCreatorOptions
        $mediaCreatorOptions.FileBased = $false
        $mediaCreatorOptions.Destination = $mediaItemFullPath
        $mediaCreatorOptions.KeepExisting = $false

        $filename = $mediaItem.Name + "." + $mediaItem.Extension
        $mediaCreator.AttachStreamToMediaItem($fileInfo.Open([System.IO.FileMode]::Open), $mediaItemFullPath, $filename, $mediaCreatorOptions)
    }
}

$items = Get-MediaItemFileBased
$items | % { ConvertTo-MediaItemDatabaseBased $_ }