Download Sitecore Media Library Files in Bulk

The latest update to the Content Export/Import Tool introduces a new feature for downloading media files in bulk.

There is a difference between downloading a Sitecore package of media items, and actually downloading the media files themselves. A Sitecore package will create a .zip file of Sitecore items that can be installed on another environment to migrate media library items. However, the actual attached images (or other media file types) cannot just be pulled from these images. If you want to download the actual attached file, you have to browse to the Media Library item and click the Download button, and there is no OOTB way to do this for multiple items, or an entire folder, at once.

I have had a few requests from clients to help them download the actual media files from Sitecore, so this seemed like it would be a useful addition to the Content Export Tool.

Media Export feature

The Media Export is easy to use and, like the other features, uses the same filters as everything else. The Start Item(s) field is used to choose the starting folder; you can exclude Children to only pull selected media files, and use the other filters as well such as date and author.

Because the Media Library often has thousands of items, you must select a Start Item(s). If you do not, you will get a warning informing you that a start item must be set. If you really want to export the entire Media Library, you can do so by setting the Start Item to /Sitecore/Media Library.

The selected images are automatically downloaded as a compressed zip folder, and can be found in your Downloads folder.

The unzipped zip folder full of images

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 $_ }