Optimize your images in Sitecore with PowerShell Extensions + Kraken or TinyPNG

Did you ever run Google PageSpeed Insights on your website and get a violation for not optimizing your images? I did, so I figured out how to fix it in a pretty painless manner. This issue comes up from time to time, so it’s important to know how to resolve it before it’s too late. No one wants to manually fix 500 images in production via downloading, optimizing, and re-attaching (looking at you, Ben). The solution I want to show you today involves optimizing images in Sitecore with PowerShell and TinyPNG.


Introducing TinyPNG for optimizing images

TinyPNG (https://tinypng.com/) is a site I visit pretty frequently in my day-to-day activities as a Sitecore Developer. Any time I upload an image to the Sitecore CMS, I try to make sure it’s optimized. That’s what TinyPNG does, and it does it well. Fortunately for me, I have a team to help me assemble content, so it’s inevitable that lots of media will be uploaded by a variety of people, some of which may not be optimized for web.

tinypng splash image

According to TinyPNG, they use “smart lossy compression techniques to reduce the file size of your PNG files. By selectively decreasing the number of colors in the image, fewer bytes are required to store the data. The effect is nearly invisible but it makes a very large difference in file size!”

Typically a lossy compression will result in tremendous file size reduction at the cost of minor visual clarity at very high resolutions. I honestly have never really noticed a visual difference in image quality when using TinyPNG, so I think their lossy algorithm must be pretty good.

Also worth noting: TinyPNG works on .jpg files as well.


Kraken.io: Another excellent solution

If you want a lossless compression (e.g., zero reduction in visual clarity), Kraken.io offers that ability.

kraken.io splash image

I remember doing a proof-of-concept integration between Kraken.io and the mediaUpload pipeline in the past. It worked out beautifully but I never ended up forking the money over for a professional Kraken subscription.

Regardless of which optimization vendor you choose, you can integrate with them easily from PowerShell.


Let’s automate them with PowerShell

Sitecore PowerShell Extensions is my new favorite toy. I can’t believe I went so long without really using it. If you’re not using this module, you should be. There’s excellent documentation and lots of activity in the #module-spe channel of the Sitecore Slack Community. After all, it’s the secret sauce to the image optimization technique I’m about to share with you!

First of all, let’s build out where we’ll install our script:

how to add script to sitecore

We’ll create a PowerShell script object under /sitecore/system/Modules/PowerShell/Script Library/My-Tenant/Content Editor/Context Menu/Media/Optimize Image. The “Show if rules are met…” field basically allows the script to be executed on these template types:

  • /sitecore/templates/System/Media/Unversioned/Image
  • /sitecore/templates/System/Media/Unversioned/Jpeg
  • /sitecore/templates/System/Media/Versioned/Image
  • /sitecore/templates/System/Media/Versioned/Jpeg

You may want to tweak that a bit depending on your intentions (e.g., allow on any media folder type, to apply this same technique to all descendant items recursively).


And now, some code

Here’s the TinyPNG flavor of this script. Add this to the Script body field of your PowerShell object in Sitecore. Keep in mind that I’m still new to PowerShell, so I apologize for any awkward code. 🙂

# Take a media item and shoot it to TinyPNG's API.  Return a URL indicating where the resulting optimized image is
function TinifyImage($mediaItem) {
    $apiKey = "TODO REPLACE WITH API KEY" # generate an API key: https://tinypng.com/developers
    $apiAuthorization = "api:$apiKey"
    $encodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($apiAuthorization))
    $basicAuthValue = "Basic $encodedCreds"
    $Headers = @{
        Authorization = $basicAuthValue
    $blobField = $mediaItem.InnerItem.Fields["blob"]
    $blobStream = $blobField.GetBlobStream()
    Try {
        $request = Invoke-RestMethod -Method Post -Uri "https://api.tinify.com/shrink" -Headers $Headers -Body $blobStream
        if ($request.output.url) {
            return $request.output.url
        else {
            Write-Error "TinyPNG request failed"
            Write-Host ($request | Format-List | Out-String)
    Catch {
        Write-Error "TinyPNG request failed"
# Sets the $mediaItem to the image at $url
function SetMedia($mediaItem, $url, $extension) {
    # download file to temporary location
    $tempFolder = "$SitecoreDataFolder\temp"
    Test-Path $tempFolder
    if ((Test-Path $tempFolder) -eq 0) {
        New-Item -ItemType Directory -Force -Path $tempFolder
    # instead of temp.png, you may want to generate a unique name here to avoid collisions
    $filePath = "$tempFolder\temp.$extension"
    Invoke-WebRequest -Uri $url -OutFile $filePath
    # set the media stream to be the file system file
    $stream = New-Object -TypeName System.IO.FileStream -ArgumentList $filePath, "Open", "Read"
    [Sitecore.Resources.Media.Media] $media = [Sitecore.Resources.Media.MediaManager]::GetMedia($mediaItem);
    $media.SetStream($stream, $extension);
    # delete temporary file
    Remove-Item $filePath
# Execute against current item
$location = get-location
$scItem = Get-Item $location
$mediaItem = New-Object "Sitecore.Data.Items.MediaItem" $scItem
$extension = $mediaItem.Extension
$url = TinifyImage($mediaItem)
if ($location) {
    Write-Host "Tiny PNG optimized url: $url"
    SetMedia $mediaItem $url $extension
} else {
    Write-Error "Tiny PNG failed"


It’s pretty simple. Given a Media Library item, post the binary to http://api.tinify.com. Take the resulting optimized image URL (returned from the API), and set the original media item’s binary to the newly optimized version.

Of course, the Kraken.io version is similar. The only difference really is that the Kraken API requires an image URL which they will then fetch and optimize. This may or may not work for you depending on your network setup. It also requires a bit of imagination to properly generate a URL resolving to an image inside of the master database (e.g., optimize the image directly in master before publishing to web). This is just a quick and dirty example of how it can be achieved:

function KrakImage($mediaItem) {
    $muo = New-Object Sitecore.Resources.Media.MediaUrlOptions
    $muo.AlwaysIncludeServerUrl = 1
    $muo.UseItemPath = 0
    $muo.AbsolutePath = 1
    # May or may not work depending on your setup.  You basically need $linkUrl to be a URL pointing to your image.
    # The trick is that your image may or may not be published, so you probably need $linkUrl to resolve to the master
    # database if possible.
    $linkUrl = [Sitecore.Resources.Media.MediaManager]::GetMediaUrl($mediaItem, $muo)
    $url = $linkUrl -replace '/sitecore/shell', ''
    # replace with your api credentials: https://kraken.io/docs/getting-started
    $krakenKey = "KRAKEN KEY"
    $krakenSecret = "KRAKEN SECRET"
    $hash = @{
        auth = @{
            api_key = $krakenKey;
            api_secret = $krakenSecret;
        wait = $TRUE;
        url = $url;
    $jsonBody = $hash | convertto-json
    Try {
        $request = Invoke-RestMethod -Method Post -Uri https://api.kraken.io/v1/url -ContentType "application/json" -Body $jsonBody
        if ($request.success -eq $TRUE) {
            return $request.kraked_url
        else {
            Write-Error "Kraken request failed"
            Write-Host ($request | Format-List | Out-String)
    Catch {
        Write-Error "Kraken request failed"
# same SetMedia method as the TinyPNG example
function SetMedia($mediaItem, $url, $extension) {
    # ...
$location = get-location
$scItem = Get-Item $location
$mediaItem = New-Object "Sitecore.Data.Items.MediaItem" $scItem
$extension = $mediaItem.Extension
$url = KrakImage($mediaItem)
if ($location) {
    Write-Host "Kraken optimized url: $url"
    SetMedia $mediaItem $url $extension
} else {
    Write-Error "Kraken.io  failed"


If you set all of this up correctly, you should now have an option like this when right clicking on an image in the Media Library:

Optimize Image script in Action

Shoutout to CJ Morgan for advocating PowerShell at BrainJocks, as well as Michael West and Adam Najmanowicz for being a tremendous help in Sitecore Slack!


Dylan McCurry

I am a certified Sitecore developer with a passion for the web. I hopped into the .NET space 5 years ago to work on enterprise-class applications and never looked back. I love building things—everything from from Legos to software that solves real problems. I have a strong foundation of backend skills, with sweet spots like security, portal solutions and APIs. Early on, before I had the benefit of SCORE, I made a lot of mistakes with Sitecore but learned a lot in the course of the struggle. I would like to support other developers by contributing my perspective on doing things “the Sitecore way,” rather than fighting the framework. Did I mention I love video games?

More posts from Dylan McCurry >

5 comments on Optimize your images in Sitecore with PowerShell Extensions + Kraken or TinyPNG

Christopher JonesApril 27, 2017 - Reply

Dylan, I stumbled across your post while searching for something else, but knew we had a specific request to do this exact thing with several thousand images. I had a far more laborious solution in mind. You just saved me a long, painful, soul-crushing slog. The only change I’m making is to create batches so I can load up a bunch at once. Thanks for sharing this.

Dylan McCurryApril 27, 2017 - Reply

Glad it worked out for you Chris!

Dylan McCurryOctober 31, 2017 - Reply

Hi Joey,

Typically I would recommend uploading images to the media library which are already optimized and of the appropriate size. The mw and mh properties instruct Sitecore to programmatically resize the images, which I believe is done via a built in .NET mechanic. I doubt that this would give crisp results.

Also, if you use TinyPNG- it is technically a lossy style compression meaning that it will compress the image at some visual clarity loss. Since it is a lossy style compression, you can run the optimization multiple times on the same image. If you do this, each subsequent optimization will lead to a slightly more pixelated image. In general, you should only ever run TinyPNG’s optimization on an image once.

Joey NovaisOctober 17, 2017 - Reply


I tried to use your solution because I have some heavy images. The problem is that when I try to use the image with parameters mw and mh I have a really pixelated image that is not usable. Do you know why it happens ?

Add a Comment

Your email address will not be published. Required fields are marked *

Or request call back