Giving Back: IIS Web Service for ExifTool

Started by clubside, July 16, 2020, 10:42:57 AM

Previous topic - Next topic

clubside

Howdy!

After trying many JavaScript libraries to get image metadata for my app and coming up short one way or another I decided to dive into ExifTool which I had planned to switch to down the line after I figured out how to Perl into my server blah blah blah...

Anyway, I couldn't wait and not finding anything ready made I took it upon myself to get it working and share what I did for anyone else looking for ExifTool's awesome functionality in an IIS environment. This is a .NET Framework implementation.

The Code

getexif.aspx

<%@ Page Language="C#" Debug="true" AutoEventWireup="true" CodeFile="getexif.aspx.cs" Inherits="_Default" %>
<asp:Literal ID="Literal1" runat="server" />

getexif.cs.aspx

using System;
using System.Text;
using System.Text.RegularExpressions;
using System.Diagnostics;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;

public partial class _Default : System.Web.UI.Page
{
    private static bool redirectStandardOutput = true;
    private static StringBuilder sb = new StringBuilder();
    protected void Page_Load(object sender, EventArgs e)
    {
        bool tableMode = false;
        string currentFolder = Regex.Replace(Request.ServerVariables["APPL_PHYSICAL_PATH"], @"\\", @"/", RegexOptions.Multiline);
        string fileSource = currentFolder + Request.QueryString["file"];
        try
        {
            if (Request.QueryString["tableMode"]!=null)
            {
                tableMode = true;
            }
        }
        catch
        {
            tableMode = false;
        }
        StringBuilder a = new StringBuilder();
        if (tableMode)
        {
            a.Append("-h ");
        }
        else
        {
            a.Append("-json ");
        }
        a.Append("-g0:1 ");
        a.Append("\"");
        a.Append(fileSource);
        a.Append("\"");
        sb = new StringBuilder();
        // Start the child process.
        Process p = new Process();
        // Redirect the output stream of the child process.
        p.StartInfo.UseShellExecute = false;
        p.StartInfo.RedirectStandardOutput = true;
        p.StartInfo.CreateNoWindow = true;
        p.StartInfo.StandardOutputEncoding = System.Text.Encoding.UTF8;
        p.StartInfo.FileName = "M:\\charles\\mover\\webservices\\exiftool.exe";
        p.StartInfo.Arguments = a.ToString();
        p.OutputDataReceived += OnOutputDataRecived;
        p.ErrorDataReceived += OnErrorDataReceived;
        p.Exited += OnExited;

        p.StartInfo.RedirectStandardOutput = redirectStandardOutput;
        p.StartInfo.RedirectStandardError = redirectStandardOutput;

        try
        {
            p.Start();
            p.BeginOutputReadLine();
            p.WaitForExit();
        }
        catch (Exception err)
        {
            sb.AppendLine("OS error: " + err.Message);
        }

        p.Close();

        Literal1.Mode = LiteralMode.Encode;
        Literal1.Mode = LiteralMode.PassThrough;
        Literal1.Mode = LiteralMode.Transform;

        Literal1.Text = sb.ToString();
        // Literal1.Text = fileSource;
    }
    private static void OnOutputDataRecived(object sender, DataReceivedEventArgs e)
    {
        //do something with your data
        sb.AppendLine(e.Data);
    }
    private static void OnErrorDataReceived(object sender, DataReceivedEventArgs e)
    {
        sb.AppendLine(e.Data);
    }
    // Handle Exited event and display process information.
    private static void OnExited(object sender, System.EventArgs e)
    {
        var process = sender as Process;
        if (process != null)
        {
            sb.AppendLine("ExitCode: " + process.ExitCode);
        }
        else
        {
            sb.AppendLine("Process exited");
        }
    }
}

The Process

getexif.aspx?file=[filename][&tableMode=true]

The filename in my environment is based off the root of the server's drive so I pass relative filenames and prepend the physical path. The querystring file= is passed to the variable fileSource with this prepending so it would be easy enough to switch this to absolute paths if that's your need.

The optional second querystring tableMode switches the standard json output to html table format.

The service calls ExifTool by absolute path only because my dev environment uses a mapped drive different from the drive on the server. It works as a relative call but figured I'd leave in the absolute path in case people install ExifTool in a single location and would rather not make a copy.

A StringBuilder takes the output as it arrives in the process and posts it as the response on end.

Another StringBuilder creates the argument list for the process. I left this in very small steps to make it easier for people to adapt to their needs. This code is basically exiftool -json -g0:1 "filename"

UTF-8 is set and the filename quoyed in case of spaces or other special characters.

Hope this is helpful and thanks again for this wonderful tool!

Chris

Phil Harvey

...where DIR is the name of a directory/folder containing the images.  On Mac/Linux/PowerShell, use single quotes (') instead of double quotes (") around arguments containing a dollar sign ($).